Compare commits
144 Commits
mvp-step1
...
feature/em
| Author | SHA1 | Date | |
|---|---|---|---|
| cff6ee5478 | |||
|
|
98fcd2eb47 | ||
|
|
dda9b7adad | ||
|
|
5663c313ea | ||
|
|
7da9b81319 | ||
| c426bbf793 | |||
| 329debaab8 | |||
|
|
994a0174f5 | ||
| 2846297112 | |||
| 5cf60ddfdc | |||
| 0de33f04bc | |||
| 472353632c | |||
|
|
1a48fb5b20 | ||
| 99d3a01991 | |||
|
|
da24972482 | ||
| ecb90e7120 | |||
|
|
784cb7a473 | ||
| 5349c46225 | |||
| 3be4402239 | |||
| 4c482ea289 | |||
| 44a73478a7 | |||
| bceac2f172 | |||
| 98842db343 | |||
| 03904a4e35 | |||
| 09b3c8df47 | |||
| 26761f6d39 | |||
| 72608a8ffd | |||
| d1ec123c8b | |||
| 4102ff5522 | |||
| 08e857884b | |||
| c0db2d230b | |||
| 2b9b772b39 | |||
| c46e8a7047 | |||
| 6e6350d6ce | |||
| 1e7d553bd6 | |||
| 35ae2539cb | |||
|
|
a614d754f4 | ||
| 46a6ba534e | |||
| 8fecde4c42 | |||
|
|
3e73799532 | ||
| 3159366560 | |||
| 5176cff2bd | |||
| 978aa723ae | |||
| 926c338f73 | |||
| 6de60c82ba | |||
| 6ed17a50e5 | |||
| 5cd6d02f6e | |||
| 276e5e9122 | |||
| 6e75a2a524 | |||
| ea6e603036 | |||
| edad30e264 | |||
| 58616100f4 | |||
| 359558bad3 | |||
| 6a6554ed1f | |||
| e20625abdb | |||
| f83dd693d5 | |||
| a464297511 | |||
| 991c8e8083 | |||
| 9ea183ff2d | |||
| 34d368b730 | |||
| 17760a6926 | |||
| 8b9cfa34c7 | |||
| 5fb8fe53bb | |||
| 5cb9375db3 | |||
| 55181f2c57 | |||
| 4577df371a | |||
| 0f154832a5 | |||
| 5951fcc108 | |||
| 7966217ac2 | |||
| 64851bd51d | |||
| 10e584d2ac | |||
| b97a728624 | |||
| 5a875e6853 | |||
| 57a4823f61 | |||
| 761eb4c13e | |||
| 354fc6868d | |||
| 2538e4526f | |||
| 26890f5b35 | |||
| 086b5e2621 | |||
| 617b060869 | |||
| a782e92bd6 | |||
| f60f97380f | |||
| cfc416fd14 | |||
| df593d2ffe | |||
| 9cd4c8a5ab | |||
|
|
ead6658455 | ||
| f6d677b51f | |||
| 25f05ab018 | |||
| 178cf33949 | |||
| b54c0f8022 | |||
| c5cc3c1a24 | |||
| 0b414fbfbe | |||
| 981d7a5062 | |||
| 33fc0b74ef | |||
| 286acc4584 | |||
| cdfb1d4310 | |||
| 6844652b5d | |||
| 9e72acf84b | |||
| 52f4af759e | |||
| 76457b6667 | |||
| e1f2afa942 | |||
| 8d5dff60f1 | |||
| 461cd26690 | |||
| 4c6b2ea844 | |||
| 418f45f997 | |||
| 51ebe99d1c | |||
| 2f1f8a60fc | |||
| 64795a80c7 | |||
| 94a17073dd | |||
| 7f7d625fdd | |||
| 6902079866 | |||
| 6aa057e590 | |||
| 2721cd60d1 | |||
| 1f8d079d4d | |||
|
|
f34dec1054 | ||
|
|
01b64f1aba | ||
|
|
3367ada343 | ||
|
|
f4ea3eaccb | ||
|
|
4adb8401d6 | ||
| df59bff6ae | |||
| 3ae685a0e2 | |||
|
|
fce56e43c3 | ||
|
|
42618602f4 | ||
| c1e50736e8 | |||
|
|
e02131846b | ||
|
|
2c3b27d9de | ||
|
|
6ccc833f7d | ||
|
|
a0ecc2eee3 | ||
| 59e6d33656 | |||
| b641c729c2 | |||
| 142cdcda38 | |||
| fc2669dabf | |||
| 8e095b51e3 | |||
| ff03490209 | |||
| 40cd9998d0 | |||
| 973ba159b4 | |||
| 063a5af822 | |||
|
|
6a06c71104 | ||
| cccff1d16d | |||
|
|
100d8de54f | ||
|
|
7c667660c0 | ||
|
|
4eb56372a5 | ||
|
|
16edd398be | ||
|
|
4e08159e6d |
2
backend/.gitignore
vendored
2
backend/.gitignore
vendored
@@ -56,6 +56,7 @@ coverage.xml
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
*.log.*
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
@@ -126,3 +127,4 @@ cython_debug/
|
||||
.vscode/
|
||||
|
||||
*.lock
|
||||
Temp/
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
FROM python:3.8
|
||||
FROM python:3.11.4
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
502
backend/Scripts/Activate.ps1
Normal file
502
backend/Scripts/Activate.ps1
Normal file
@@ -0,0 +1,502 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
||||
|
||||
# SIG # Begin signature block
|
||||
# MIIvIgYJKoZIhvcNAQcCoIIvEzCCLw8CAQExDzANBglghkgBZQMEAgEFADB5Bgor
|
||||
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
|
||||
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnL745ElCYk8vk
|
||||
# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCE8MwggWQMIIDeKADAgECAhAFmxtXno4h
|
||||
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
|
||||
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
|
||||
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
|
||||
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
|
||||
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
|
||||
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
||||
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
|
||||
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
|
||||
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
|
||||
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
|
||||
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
|
||||
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
|
||||
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
|
||||
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
|
||||
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
|
||||
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
|
||||
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
|
||||
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
|
||||
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
|
||||
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
|
||||
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
|
||||
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
|
||||
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
|
||||
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
|
||||
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
|
||||
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
|
||||
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
|
||||
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
|
||||
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
|
||||
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
|
||||
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
|
||||
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
|
||||
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
|
||||
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
|
||||
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
|
||||
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
|
||||
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
|
||||
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
|
||||
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
|
||||
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
|
||||
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
|
||||
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
|
||||
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
|
||||
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
|
||||
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
|
||||
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
|
||||
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
|
||||
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
|
||||
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
|
||||
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
|
||||
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
|
||||
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
|
||||
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
|
||||
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
|
||||
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
|
||||
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
|
||||
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
|
||||
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
|
||||
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
|
||||
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
|
||||
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
|
||||
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
|
||||
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
|
||||
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
|
||||
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
|
||||
# eE4wggd3MIIFX6ADAgECAhAHHxQbizANJfMU6yMM0NHdMA0GCSqGSIb3DQEBCwUA
|
||||
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
|
||||
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
|
||||
# ODQgMjAyMSBDQTEwHhcNMjIwMTE3MDAwMDAwWhcNMjUwMTE1MjM1OTU5WjB8MQsw
|
||||
# CQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQHEwlCZWF2ZXJ0b24x
|
||||
# IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQDExpQ
|
||||
# eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
|
||||
# ADCCAgoCggIBAKgc0BTT+iKbtK6f2mr9pNMUTcAJxKdsuOiSYgDFfwhjQy89koM7
|
||||
# uP+QV/gwx8MzEt3c9tLJvDccVWQ8H7mVsk/K+X+IufBLCgUi0GGAZUegEAeRlSXx
|
||||
# xhYScr818ma8EvGIZdiSOhqjYc4KnfgfIS4RLtZSrDFG2tN16yS8skFa3IHyvWdb
|
||||
# D9PvZ4iYNAS4pjYDRjT/9uzPZ4Pan+53xZIcDgjiTwOh8VGuppxcia6a7xCyKoOA
|
||||
# GjvCyQsj5223v1/Ig7Dp9mGI+nh1E3IwmyTIIuVHyK6Lqu352diDY+iCMpk9Zanm
|
||||
# SjmB+GMVs+H/gOiofjjtf6oz0ki3rb7sQ8fTnonIL9dyGTJ0ZFYKeb6BLA66d2GA
|
||||
# LwxZhLe5WH4Np9HcyXHACkppsE6ynYjTOd7+jN1PRJahN1oERzTzEiV6nCO1M3U1
|
||||
# HbPTGyq52IMFSBM2/07WTJSbOeXjvYR7aUxK9/ZkJiacl2iZI7IWe7JKhHohqKuc
|
||||
# eQNyOzxTakLcRkzynvIrk33R9YVqtB4L6wtFxhUjvDnQg16xot2KVPdfyPAWd81w
|
||||
# tZADmrUtsZ9qG79x1hBdyOl4vUtVPECuyhCxaw+faVjumapPUnwo8ygflJJ74J+B
|
||||
# Yxf6UuD7m8yzsfXWkdv52DjL74TxzuFTLHPyARWCSCAbzn3ZIly+qIqDAgMBAAGj
|
||||
# ggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4E
|
||||
# FgQUt/1Teh2XDuUj2WW3siYWJgkZHA8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
|
||||
# MAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRp
|
||||
# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI
|
||||
# QTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v
|
||||
# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex
|
||||
# LmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
|
||||
# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUF
|
||||
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6
|
||||
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu
|
||||
# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZI
|
||||
# hvcNAQELBQADggIBABxv4AeV/5ltkELHSC63fXAFYS5tadcWTiNc2rskrNLrfH1N
|
||||
# s0vgSZFoQxYBFKI159E8oQQ1SKbTEubZ/B9kmHPhprHya08+VVzxC88pOEvz68nA
|
||||
# 82oEM09584aILqYmj8Pj7h/kmZNzuEL7WiwFa/U1hX+XiWfLIJQsAHBla0i7QRF2
|
||||
# de8/VSF0XXFa2kBQ6aiTsiLyKPNbaNtbcucaUdn6vVUS5izWOXM95BSkFSKdE45O
|
||||
# q3FForNJXjBvSCpwcP36WklaHL+aHu1upIhCTUkzTHMh8b86WmjRUqbrnvdyR2yd
|
||||
# I5l1OqcMBjkpPpIV6wcc+KY/RH2xvVuuoHjlUjwq2bHiNoX+W1scCpnA8YTs2d50
|
||||
# jDHUgwUo+ciwpffH0Riq132NFmrH3r67VaN3TuBxjI8SIZM58WEDkbeoriDk3hxU
|
||||
# 8ZWV7b8AW6oyVBGfM06UgkfMb58h+tJPrFx8VI/WLq1dTqMfZOm5cuclMnUHs2uq
|
||||
# rRNtnV8UfidPBL4ZHkTcClQbCoz0UbLhkiDvIS00Dn+BBcxw/TKqVL4Oaz3bkMSs
|
||||
# M46LciTeucHY9ExRVt3zy7i149sd+F4QozPqn7FrSVHXmem3r7bjyHTxOgqxRCVa
|
||||
# 18Vtx7P/8bYSBeS+WHCKcliFCecspusCDSlnRUjZwyPdP0VHxaZg2unjHY3rMYIa
|
||||
# tTCCGrECAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu
|
||||
# Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJT
|
||||
# QTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAHHxQbizANJfMU6yMM0NHdMA0GCWCGSAFl
|
||||
# AwQCAQUAoIHIMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC
|
||||
# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBnAZ6P7YvTwq0fbF62
|
||||
# o7E75R0LxsW5OtyYiFESQckLhjBcBgorBgEEAYI3AgEMMU4wTKBGgEQAQgB1AGkA
|
||||
# bAB0ADoAIABSAGUAbABlAGEAcwBlAF8AdgAzAC4AMQAxAC4ANABfADIAMAAyADMA
|
||||
# MAA2ADAANwAuADAAMaECgAAwDQYJKoZIhvcNAQEBBQAEggIAVdxtEr9NH8SoVTzT
|
||||
# o/jdr3t1yqExSecge3YGCu9USfMqLtmCKzG5r2rf3xZkJ6CpvmHwji3FUY6Hl991
|
||||
# Ttd0eEEpjeEse9gotnojgHTQACJntGuPcK+65jIQYNvp3JIuczjTW0JjWkJf4lqI
|
||||
# hVS6rEc00D/0NsUF9BbNkjNZ0AUQeOWe2WZJnqRRFN4U3pToN51NDjpEtRjlNTkc
|
||||
# SzoNO7ZyEsSXkNenlgbgS1yXEQ8v4bbnbPyyL+2yWMG1QsLv6M3OV0kXx9aow1r5
|
||||
# gZ1mCjBkbtWKH58WVBoepUaPYTjFBWCT2pDrorbg6cguwBdyz7s8X+WlCD4ycFfW
|
||||
# o95x7u1W9RwPPPppszr8Pd4jZSbEXEQ/G9Ke5NvTvNmK93b7/kySfNYfwW2meP6E
|
||||
# JIc0R9DMSZlK+ChtU5mmvo4e6YQTLXIXQhPIz7jVNlUjXMJX7WALjE72EDdC5MpQ
|
||||
# ygW7wue6EhjlUVXT4pEIySCGaXxUzRi1oh+Q+Jbe3rDvhSPZUWzCqEtOkJ35dLYh
|
||||
# D9Rahi2BM1qaepfu1wVtSXbVbc0SDPjloojEmTyDnk61u5epo0E0oHqNAU8t1ZTN
|
||||
# +Guptl/agMp52uRsaC5Bi276icqRtclfx9E4SfJEiw7xRlImCclMpw2dRsyzIrpb
|
||||
# MKpWDAno4rClgYS3M9lqQ71RlXehghc+MIIXOgYKKwYBBAGCNwMDATGCFyowghcm
|
||||
# BgkqhkiG9w0BBwKgghcXMIIXEwIBAzEPMA0GCWCGSAFlAwQCAQUAMHgGCyqGSIb3
|
||||
# DQEJEAEEoGkEZzBlAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgsPGH
|
||||
# UIiYgGXi/94WNZrP+V1kV/B5SVJn3ck+XzTJ0aACEQCJ79BpOkCDCW06IgZOU3EQ
|
||||
# GA8yMDIzMDYwNzA1NTc0NFqgghMHMIIGwDCCBKigAwIBAgIQDE1pckuU+jwqSj0p
|
||||
# B4A9WjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
|
||||
# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5
|
||||
# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIyMDkyMTAwMDAwMFoXDTMzMTEy
|
||||
# MTIzNTk1OVowRjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSQwIgYD
|
||||
# VQQDExtEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMiAtIDIwggIiMA0GCSqGSIb3DQEB
|
||||
# AQUAA4ICDwAwggIKAoICAQDP7KUmOsap8mu7jcENmtuh6BSFdDMaJqzQHFUeHjZt
|
||||
# vJJVDGH0nQl3PRWWCC9rZKT9BoMW15GSOBwxApb7crGXOlWvM+xhiummKNuQY1y9
|
||||
# iVPgOi2Mh0KuJqTku3h4uXoW4VbGwLpkU7sqFudQSLuIaQyIxvG+4C99O7HKU41A
|
||||
# gx7ny3JJKB5MgB6FVueF7fJhvKo6B332q27lZt3iXPUv7Y3UTZWEaOOAy2p50dIQ
|
||||
# kUYp6z4m8rSMzUy5Zsi7qlA4DeWMlF0ZWr/1e0BubxaompyVR4aFeT4MXmaMGgok
|
||||
# vpyq0py2909ueMQoP6McD1AGN7oI2TWmtR7aeFgdOej4TJEQln5N4d3CraV++C0b
|
||||
# H+wrRhijGfY59/XBT3EuiQMRoku7mL/6T+R7Nu8GRORV/zbq5Xwx5/PCUsTmFnta
|
||||
# fqUlc9vAapkhLWPlWfVNL5AfJ7fSqxTlOGaHUQhr+1NDOdBk+lbP4PQK5hRtZHi7
|
||||
# mP2Uw3Mh8y/CLiDXgazT8QfU4b3ZXUtuMZQpi+ZBpGWUwFjl5S4pkKa3YWT62SBs
|
||||
# GFFguqaBDwklU/G/O+mrBw5qBzliGcnWhX8T2Y15z2LF7OF7ucxnEweawXjtxojI
|
||||
# sG4yeccLWYONxu71LHx7jstkifGxxLjnU15fVdJ9GSlZA076XepFcxyEftfO4tQ6
|
||||
# dwIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYD
|
||||
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI
|
||||
# AYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQW
|
||||
# BBRiit7QYfyPMRTtlwvNPSqUFN9SnDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8v
|
||||
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2
|
||||
# VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcw
|
||||
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8v
|
||||
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hB
|
||||
# MjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQBVqioa80bz
|
||||
# eFc3MPx140/WhSPx/PmVOZsl5vdyipjDd9Rk/BX7NsJJUSx4iGNVCUY5APxp1Mqb
|
||||
# KfujP8DJAJsTHbCYidx48s18hc1Tna9i4mFmoxQqRYdKmEIrUPwbtZ4IMAn65C3X
|
||||
# CYl5+QnmiM59G7hqopvBU2AJ6KO4ndetHxy47JhB8PYOgPvk/9+dEKfrALpfSo8a
|
||||
# OlK06r8JSRU1NlmaD1TSsht/fl4JrXZUinRtytIFZyt26/+YsiaVOBmIRBTlClmi
|
||||
# a+ciPkQh0j8cwJvtfEiy2JIMkU88ZpSvXQJT657inuTTH4YBZJwAwuladHUNPeF5
|
||||
# iL8cAZfJGSOA1zZaX5YWsWMMxkZAO85dNdRZPkOaGK7DycvD+5sTX2q1x+DzBcNZ
|
||||
# 3ydiK95ByVO5/zQQZ/YmMph7/lxClIGUgp2sCovGSxVK05iQRWAzgOAj3vgDpPZF
|
||||
# R+XOuANCR+hBNnF3rf2i6Jd0Ti7aHh2MWsgemtXC8MYiqE+bvdgcmlHEL5r2X6cn
|
||||
# l7qWLoVXwGDneFZ/au/ClZpLEQLIgpzJGgV8unG1TnqZbPTontRamMifv427GFxD
|
||||
# 9dAq6OJi7ngE273R+1sKqHB+8JeEeOMIA11HLGOoJTiXAdI/Otrl5fbmm9x+LMz/
|
||||
# F0xNAKLY1gEOuIvu5uByVYksJxlh9ncBjDCCBq4wggSWoAMCAQICEAc2N7ckVHzY
|
||||
# R6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoT
|
||||
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UE
|
||||
# AxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3
|
||||
# MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
|
||||
# bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2
|
||||
# IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
||||
# AMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjz
|
||||
# aPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3E
|
||||
# F3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYnc
|
||||
# fGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8O
|
||||
# pWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROp
|
||||
# VymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4i
|
||||
# FNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmif
|
||||
# tkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0
|
||||
# UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9Ne
|
||||
# S3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCj
|
||||
# WAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTAS
|
||||
# BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57I
|
||||
# bzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMC
|
||||
# AYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUF
|
||||
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6
|
||||
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0
|
||||
# MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp
|
||||
# Q2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCG
|
||||
# SAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAY
|
||||
# LhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQx
|
||||
# Z822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf
|
||||
# 7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDV
|
||||
# inF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7
|
||||
# +6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJ
|
||||
# D5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvk
|
||||
# OHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJG
|
||||
# nXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimG
|
||||
# sJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38A
|
||||
# C+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d
|
||||
# 2zc4GqEr9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqG
|
||||
# SIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
|
||||
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFz
|
||||
# c3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTla
|
||||
# MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT
|
||||
# EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9v
|
||||
# dCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8
|
||||
# MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauy
|
||||
# efLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34Lz
|
||||
# B4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+x
|
||||
# embud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhA
|
||||
# kHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1Lyu
|
||||
# GwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2
|
||||
# PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37A
|
||||
# lLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD7
|
||||
# 6GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/
|
||||
# ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXA
|
||||
# j6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTAD
|
||||
# AQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF
|
||||
# 66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEE
|
||||
# bTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYB
|
||||
# BQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
|
||||
# ZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGln
|
||||
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAI
|
||||
# MAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979X
|
||||
# B72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4k
|
||||
# vFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU
|
||||
# 53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pc
|
||||
# VIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5v
|
||||
# Iy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2
|
||||
# MIIDcgIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j
|
||||
# LjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBU
|
||||
# aW1lU3RhbXBpbmcgQ0ECEAxNaXJLlPo8Kko9KQeAPVowDQYJYIZIAWUDBAIBBQCg
|
||||
# gdEwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0y
|
||||
# MzA2MDcwNTU3NDRaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFPOHIk2GM4KSNamU
|
||||
# vL2Plun+HHxzMC8GCSqGSIb3DQEJBDEiBCAZCWWaBjTHAZnsndSxyxCaZSOrTyqo
|
||||
# O35hv3VOlS9KHDA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCDH9OG+MiiJIKviJjq+
|
||||
# GsT8T+Z4HC1k0EyAdVegI7W2+jANBgkqhkiG9w0BAQEFAASCAgBt92vHxbHXh4Z2
|
||||
# yl+aTo7PltgPhZhoQCWg+gDSyySEqkDN+kTuoW3ROuMjR1JR0htJOwVqnmI/enhW
|
||||
# r8VJiDKfGOGupHEfzAlMaIIC+K+C3sSoeaRR1aiOWrUA/oPpJIgwXyfo65Hf07qf
|
||||
# wdn//4y5zv6oMdHNtpSfFgibze5BjNRAgOUxl9rvKArEN7B+WTCnvLWw/EJe48MQ
|
||||
# B0zUbVFIORQUHlLnCL07JGRSN5bHaMtnn5eEwZFC9522kJaHyLrmfeP4jZLMhjhn
|
||||
# fGxv69HVzggM8CpjpQA8l8hh6Il48TDMZpdqkxwjoRmJVwt3hwTrfuE11NFrXEAD
|
||||
# 8dAAta6N/M722c3BE6UxM2R4QXyV05BL6e4jVJm1aR1ebUVS4nZVZ/jbCexR/+vx
|
||||
# mfSh1SezU3KlgRMDrLF+El883BFoe/99p4/QjjnELhn41lPPAYGefhMI9ioYZULQ
|
||||
# xMyG6qIPA8s2tnYIL/AKvh7SUgFVtOsTKbTFXMMr20sipBQQFUOb8ZD+8u4Iyc4M
|
||||
# UC4d2S6z9zwlPbSr1lk9m3R8rl+j2/VkB1S21nqda3xWFk/+n/2oEJe4gUkCiQxs
|
||||
# qFaykkcAhWYdZVRRNM89ZF23DeYAEkUEaD2M1ld0CZNtvtPNmv/NZV/Xbb3H0RPR
|
||||
# yDBB2JQI1BbEjl7HWy616MUsAqWA+Q==
|
||||
# SIG # End signature block
|
||||
69
backend/Scripts/activate
Normal file
69
backend/Scripts/activate
Normal file
@@ -0,0 +1,69 @@
|
||||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV="C:\project\Kintone\KintoneAppBuilder\backend"
|
||||
export VIRTUAL_ENV
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/Scripts:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1="(backend) ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT="(backend) "
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
34
backend/Scripts/activate.bat
Normal file
34
backend/Scripts/activate.bat
Normal file
@@ -0,0 +1,34 @@
|
||||
@echo off
|
||||
|
||||
rem This file is UTF-8 encoded, so we need to update the current code page while executing it
|
||||
for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do (
|
||||
set _OLD_CODEPAGE=%%a
|
||||
)
|
||||
if defined _OLD_CODEPAGE (
|
||||
"%SystemRoot%\System32\chcp.com" 65001 > nul
|
||||
)
|
||||
|
||||
set VIRTUAL_ENV=C:\project\Kintone\KintoneAppBuilder\backend
|
||||
|
||||
if not defined PROMPT set PROMPT=$P$G
|
||||
|
||||
if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT%
|
||||
if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT=%PROMPT%
|
||||
set PROMPT=(backend) %PROMPT%
|
||||
|
||||
if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
|
||||
set PYTHONHOME=
|
||||
|
||||
if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
|
||||
if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%
|
||||
|
||||
set PATH=%VIRTUAL_ENV%\Scripts;%PATH%
|
||||
set VIRTUAL_ENV_PROMPT=(backend)
|
||||
|
||||
:END
|
||||
if defined _OLD_CODEPAGE (
|
||||
"%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul
|
||||
set _OLD_CODEPAGE=
|
||||
)
|
||||
22
backend/Scripts/deactivate.bat
Normal file
22
backend/Scripts/deactivate.bat
Normal file
@@ -0,0 +1,22 @@
|
||||
@echo off
|
||||
|
||||
if defined _OLD_VIRTUAL_PROMPT (
|
||||
set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
|
||||
)
|
||||
set _OLD_VIRTUAL_PROMPT=
|
||||
|
||||
if defined _OLD_VIRTUAL_PYTHONHOME (
|
||||
set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%"
|
||||
set _OLD_VIRTUAL_PYTHONHOME=
|
||||
)
|
||||
|
||||
if defined _OLD_VIRTUAL_PATH (
|
||||
set "PATH=%_OLD_VIRTUAL_PATH%"
|
||||
)
|
||||
|
||||
set _OLD_VIRTUAL_PATH=
|
||||
|
||||
set VIRTUAL_ENV=
|
||||
set VIRTUAL_ENV_PROMPT=
|
||||
|
||||
:END
|
||||
BIN
backend/Scripts/pip.exe
Normal file
BIN
backend/Scripts/pip.exe
Normal file
Binary file not shown.
BIN
backend/Scripts/pip3.11.exe
Normal file
BIN
backend/Scripts/pip3.11.exe
Normal file
Binary file not shown.
BIN
backend/Scripts/pip3.exe
Normal file
BIN
backend/Scripts/pip3.exe
Normal file
Binary file not shown.
BIN
backend/Scripts/python.exe
Normal file
BIN
backend/Scripts/python.exe
Normal file
Binary file not shown.
BIN
backend/Scripts/pythonw.exe
Normal file
BIN
backend/Scripts/pythonw.exe
Normal file
Binary file not shown.
37
backend/Temp/alc_runtime.js
Normal file
37
backend/Temp/alc_runtime.js
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -29,18 +29,19 @@ async def login(
|
||||
else:
|
||||
permissions = "user"
|
||||
access_token = security.create_access_token(
|
||||
data={"sub": user.email, "permissions": permissions},
|
||||
data={"sub": user.id, "permissions": permissions},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
return {"access_token": access_token, "token_type": "bearer","user_name":user.first_name + " " + user.last_name}
|
||||
|
||||
|
||||
@r.post("/signup")
|
||||
async def signup(
|
||||
firstname:str, lastname:str,
|
||||
db=Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()
|
||||
):
|
||||
user = sign_up_new_user(db, form_data.username, form_data.password)
|
||||
user = sign_up_new_user(db, form_data.username, form_data.password,firstname,lastname)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
@@ -56,8 +57,8 @@ async def signup(
|
||||
else:
|
||||
permissions = "user"
|
||||
access_token = security.create_access_token(
|
||||
data={"sub": user.email, "permissions": permissions},
|
||||
data={"sub": user.id, "permissions": permissions},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
return {"access_token": access_token, "token_type": "bearer","user_name":user.first_name + " " + user.last_name}
|
||||
|
||||
@@ -5,33 +5,150 @@ import pandas as pd
|
||||
import json
|
||||
import httpx
|
||||
import deepdiff
|
||||
import app.core.config as c
|
||||
|
||||
import app.core.config as config
|
||||
import os
|
||||
from pathlib import Path
|
||||
from app.db.session import SessionLocal
|
||||
from app.db.crud import get_flows_by_app,get_activedomain,get_kintoneformat
|
||||
from app.core.auth import get_current_active_user,get_current_user
|
||||
from app.core.apiexception import APIException
|
||||
|
||||
kinton_router = r = APIRouter()
|
||||
|
||||
def getfieldsfromexcel(df):
|
||||
def getkintoneenv(user = Depends(get_current_user)):
|
||||
db = SessionLocal()
|
||||
domain = get_activedomain(db, user.id)
|
||||
db.close()
|
||||
kintoneevn = config.KINTONE_ENV(domain)
|
||||
return kintoneevn
|
||||
|
||||
|
||||
def getkintoneformat():
|
||||
db = SessionLocal()
|
||||
formats = get_kintoneformat(db)
|
||||
db.close()
|
||||
return formats
|
||||
|
||||
|
||||
def createkintonefields(property,value,trueformat):
|
||||
p = []
|
||||
if(property=="options"):
|
||||
o=[]
|
||||
for v in value.split(','):
|
||||
o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
||||
p.append(f"\"options\":{{{','.join(o)}}}")
|
||||
elif(property =="expression"):
|
||||
p.append(f"\"hideExpression\":true")
|
||||
p.append(f"\"expression\":\"{value.split(':')[1]}\"")
|
||||
elif(property =="required" or property =="unique" or property =="defaultNowValue" or property =="hideExpression" or property =="digit"):
|
||||
if str(value) == trueformat:
|
||||
p.append(f"\"{property}\":true")
|
||||
else:
|
||||
p.append(f"\"{property}\":false")
|
||||
elif(property =="protocol"):
|
||||
if(value == "メールアドレス"):
|
||||
p.append("\"protocol\":\"MAIL\"")
|
||||
elif(value == "Webサイト"):
|
||||
p.append("\"protocol\":\"WEB\"")
|
||||
elif(value == "電話番号"):
|
||||
p.append("\"protocol\":\"CALL\"")
|
||||
else:
|
||||
p.append(f"\"{property}\":\"{value}\"")
|
||||
return p
|
||||
|
||||
def getfieldsfromexcel(df,mapping):
|
||||
startrow = mapping.startrow
|
||||
startcolumn = mapping.startcolumn
|
||||
typecolumn = mapping.typecolumn
|
||||
codecolumn = mapping.codecolumn
|
||||
property = mapping.field.split(",")
|
||||
trueformat = mapping.trueformat
|
||||
appname = df.iloc[0,2]
|
||||
col=[]
|
||||
for row in range(5,len(df)):
|
||||
if pd.isna(df.iloc[row,1]):
|
||||
for row in range(startrow,len(df)):
|
||||
if pd.isna(df.iloc[row,startcolumn]):
|
||||
break
|
||||
if not df.iloc[row,3] in c.KINTONE_FIELD_TYPE:
|
||||
if not df.iloc[row,typecolumn] in config.KINTONE_FIELD_TYPE:
|
||||
continue
|
||||
p=[]
|
||||
for column in range(1,7):
|
||||
for column in range(startcolumn,startcolumn + len(property)):
|
||||
if(not pd.isna(df.iloc[row,column])):
|
||||
if(property[column-1]=="options"):
|
||||
o=[]
|
||||
for v in df.iloc[row,column].split(','):
|
||||
o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
||||
p.append(f"\"{property[column-1]}\":{{{','.join(o)}}}")
|
||||
elif(property[column-1]=="required"):
|
||||
p.append(f"\"{property[column-1]}\":{df.iloc[row,column]}")
|
||||
propertyname =property[column-1]
|
||||
if(propertyname.find("[") == 0):
|
||||
continue
|
||||
elif (propertyname =="remark"):
|
||||
if (df.iloc[row,column].find("|") !=-1):
|
||||
propertyname = "options"
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
if (df.iloc[row,column] == "メールアドレス" or df.iloc[row,column] == "Webサイト" or df.iloc[row,column] == "電話番号"):
|
||||
propertyname = "protocol"
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
if (df.iloc[row,column].find("桁区切り") !=-1):
|
||||
propertyname = "digit"
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
if (df.iloc[row,column].find("前単位") !=-1):
|
||||
propertyname = "unitPosition"
|
||||
p = p + createkintonefields(propertyname, "BEFORE",trueformat)
|
||||
if (df.iloc[row,column].find("後単位") !=-1):
|
||||
propertyname = "unitPosition"
|
||||
p = p + createkintonefields(propertyname, "AFTER",trueformat)
|
||||
if (df.iloc[row,column].find("単位「") !=-1):
|
||||
propertyname = "unit"
|
||||
ids = df.iloc[row,column].index("単位「")
|
||||
ide = df.iloc[row,column].index("」")
|
||||
unit = df.iloc[row,column][ids+3:ide]
|
||||
p = p + createkintonefields(propertyname, unit,trueformat)
|
||||
else:
|
||||
continue
|
||||
elif(propertyname =="mixValue"):
|
||||
if(df.iloc[row,column].find("レコード登録時の日") != -1):
|
||||
propertyname = "defaultNowValue"
|
||||
df.iloc[row,column] = trueformat
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
elif(df.iloc[row,column].find("計:") != -1):
|
||||
propertyname = "expression"
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
elif(df.iloc[row,column] !=""):
|
||||
propertyname = "defaultValue"
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
else:
|
||||
continue
|
||||
elif(propertyname=="max" or propertyname == "min"):
|
||||
if(df.iloc[row,typecolumn] == "NUMBER"):
|
||||
propertyname = property[column-1] + "Value"
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
else:
|
||||
propertyname = property[column-1] + "Length"
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
else:
|
||||
p.append(f"\"{property[column-1]}\":\"{df.iloc[row,column]}\"")
|
||||
col.append(f"\"{df.iloc[row,2]}\":{{{','.join(p)}}}")
|
||||
fields = ",".join(col).replace("False","false").replace("True","true")
|
||||
p = p + createkintonefields(propertyname, df.iloc[row,column],trueformat)
|
||||
|
||||
# if(propertyname=="options"):
|
||||
# o=[]
|
||||
# for v in df.iloc[row,column].split(','):
|
||||
# o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
||||
# p.append(f"\"options\":{{{','.join(o)}}}")
|
||||
# elif(propertyname=="expression"):
|
||||
# p.append(f"\"hideExpression\":true")
|
||||
# p.append(f"\"expression\":{df.iloc[row,column].split(':')[1]}")
|
||||
# elif(propertyname=="required" or propertyname =="unique" or propertyname=="defaultNowValue" or propertyname=="hideExpression" or propertyname=="digit"):
|
||||
# if (df.iloc[row,column] == trueformat):
|
||||
# p.append(f"\"{propertyname}\":true")
|
||||
# else:
|
||||
# p.append(f"\"{propertyname}\":false")
|
||||
# elif(propertyname =="protocol"):
|
||||
# if(df.iloc[row,column] == "メールアドレス"):
|
||||
# p.append("\"protocol\":\"MAIL\"")
|
||||
# elif(df.iloc[row,column] == "Webサイト"):
|
||||
# p.append("\"protocol\":\"WEB\"")
|
||||
# elif(df.iloc[row,column] == "電話番号"):
|
||||
# p.append("\"protocol\":\"CALL\"")
|
||||
# else:
|
||||
# p.append(f"\"{propertyname}\":\"{df.iloc[row,column]}\"")
|
||||
|
||||
|
||||
col.append(f"\"{df.iloc[row,codecolumn]}\":{{{','.join(p)}}}")
|
||||
fields = ",".join(col).replace("\\", "\\\\")
|
||||
return json.loads(f"{{{fields}}}")
|
||||
|
||||
def getsettingfromexcel(df):
|
||||
@@ -39,10 +156,10 @@ def getsettingfromexcel(df):
|
||||
des = df.iloc[2,2]
|
||||
return {"name":appname,"description":des}
|
||||
|
||||
def getsettingfromkintone(app:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
def getsettingfromkintone(app:str,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
params = {"app":app}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/app/settings.json"
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/app/settings.json"
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
|
||||
@@ -54,24 +171,24 @@ def analysesettings(excel,kintone):
|
||||
updatesettings[key] = excel[key]
|
||||
return updatesettings
|
||||
|
||||
def createkintoneapp(name:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
def createkintoneapp(name:str,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
data = {"name":name}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app.json"
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app.json"
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
return r.json()
|
||||
|
||||
def updateappsettingstokintone(app:str,updates:dict):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/settings.json"
|
||||
def updateappsettingstokintone(app:str,updates:dict,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/settings.json"
|
||||
data = {"app":app}
|
||||
data.update(updates)
|
||||
r = httpx.put(url,headers=headers,data=json.dumps(data))
|
||||
return r.json()
|
||||
|
||||
def addfieldstokintone(app:str,fields:dict,revision:str = None):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/form/fields.json"
|
||||
def addfieldstokintone(app:str,fields:dict,c:config.KINTONE_ENV,revision:str = None):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
|
||||
if revision != None:
|
||||
data = {"app":app,"revision":revision,"properties":fields}
|
||||
else:
|
||||
@@ -79,32 +196,32 @@ def addfieldstokintone(app:str,fields:dict,revision:str = None):
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
return r.json()
|
||||
|
||||
def updatefieldstokintone(app:str,revision:str,fields:dict):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/form/fields.json"
|
||||
def updatefieldstokintone(app:str,revision:str,fields:dict,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
|
||||
data = {"app":app,"properties":fields}
|
||||
r = httpx.put(url,headers=headers,data=json.dumps(data))
|
||||
return r.json()
|
||||
|
||||
def deletefieldsfromkintone(app:str,revision:str,fields:dict):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/form/fields.json"
|
||||
def deletefieldsfromkintone(app:str,revision:str,fields:dict,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
|
||||
params = {"app":app,"revision":revision,"fields":fields}
|
||||
#r = httpx.delete(url,headers=headers,content=json.dumps(params))
|
||||
r = httpx.request(method="DELETE",url=url,headers=headers,content=json.dumps(params))
|
||||
return r.json()
|
||||
|
||||
def deoployappfromkintone(app:str,revision:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/deploy.json"
|
||||
def deoployappfromkintone(app:str,revision:str,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/deploy.json"
|
||||
data = {"apps":[{"app":app,"revision":revision}],"revert": False}
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
return r.json
|
||||
|
||||
def getfieldsfromkintone(app):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
def getfieldsfromkintone(app:str,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
params = {"app":app}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/app/form/fields.json"
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/app/form/fields.json"
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
|
||||
@@ -116,22 +233,22 @@ def analysefields(excel,kintone):
|
||||
adds = excel.keys() - kintone.keys()
|
||||
dels = kintone.keys() - excel.keys()
|
||||
for key in updates:
|
||||
for p in property:
|
||||
if excel[key].get(p) != None and kintone[key][p] != excel[key][p]:
|
||||
for p in config.KINTONE_FIELD_PROPERTY:
|
||||
if excel[key].get(p) != None and kintone[key].get(p) != None and kintone[key][p] != excel[key][p]:
|
||||
updatefields[key] = excel[key]
|
||||
break
|
||||
for key in adds:
|
||||
addfields[key] = excel[key]
|
||||
for key in dels:
|
||||
if kintone[key]["type"] in c.KINTONE_FIELD_TYPE:
|
||||
if kintone[key]["type"] in config.KINTONE_FIELD_TYPE:
|
||||
delfields.append(key)
|
||||
|
||||
return {"update":updatefields,"add":addfields,"del":delfields}
|
||||
|
||||
def getprocessfromkintone(app:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
def getprocessfromkintone(app:str,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
params = {"app":app}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/app/status.json"
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/app/status.json"
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
|
||||
@@ -195,27 +312,75 @@ def analysprocess(excel,kintone):
|
||||
# return True
|
||||
return diff
|
||||
|
||||
def updateprocesstokintone(app:str,process:dict):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/status.json"
|
||||
def updateprocesstokintone(app:str,process:dict,c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/status.json"
|
||||
data = {"app":app,"enable":True}
|
||||
data.update(process)
|
||||
r = httpx.put(url,headers=headers,data=json.dumps(data))
|
||||
return r.json()
|
||||
|
||||
def getkintoneusers():
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
def getkintoneusers(c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}/v1/users.json"
|
||||
r = httpx.get(url,headers=headers)
|
||||
return r.json()
|
||||
|
||||
def getkintoneorgs():
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
def getkintoneorgs(c:config.KINTONE_ENV):
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
params = {"code":c.KINTONE_USER}
|
||||
url = f"{c.BASE_URL}/v1/user/organizations.json"
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
|
||||
def uploadkintonefiles(file,c:config.KINTONE_ENV):
|
||||
if (file.endswith('alc_runtime.js') and config.DEPLOY_MODE == "DEV"):
|
||||
return {'fileKey':file}
|
||||
upload_files = {'file': open(file,'rb')}
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
data ={'name':'file','filename':os.path.basename(file)}
|
||||
url = f"{c.BASE_URL}/k/v1/file.json"
|
||||
r = httpx.post(url,headers=headers,data=data,files=upload_files)
|
||||
return r.json()
|
||||
|
||||
def updateappjscss(app,uploads,c:config.KINTONE_ENV):
|
||||
dsjs = []
|
||||
dscss = []
|
||||
for upload in uploads:
|
||||
for key in upload:
|
||||
if key.endswith('.js'):
|
||||
if (key.endswith('alc_runtime.js') and config.DEPLOY_MODE == "DEV"):
|
||||
dsjs.append({'type':'URL','url':config.DEPLOY_JS_URL})
|
||||
else:
|
||||
dsjs.append({'type':'FILE','file':{'fileKey':upload[key]}})
|
||||
elif key.endswith('.css'):
|
||||
dscss.append({'type':'FILE','file':{'fileKey':upload[key]}})
|
||||
ds ={'js':dsjs,'css':dscss}
|
||||
mb ={'js':[],'css':[]}
|
||||
data = {'app':app,'scope':'ALL','desktop':ds,'mobile':mb}
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/customize.json"
|
||||
print(data)
|
||||
r = httpx.put(url,headers=headers,data=json.dumps(data))
|
||||
return r.json()
|
||||
|
||||
def createappjs(domainid,app):
|
||||
db = SessionLocal()
|
||||
flows = get_flows_by_app(db,domainid,app)
|
||||
db.close()
|
||||
content={}
|
||||
for flow in flows:
|
||||
content[flow.eventid] = {'flowid':flow.flowid,'name':flow.name,'content':flow.content}
|
||||
js = 'const alcflow=' + json.dumps(content)
|
||||
scriptdir = Path(__file__).resolve().parent
|
||||
rootdir = scriptdir.parent.parent.parent.parent
|
||||
fpath = os.path.join(rootdir,"Temp",f"alc_setting_{app}.js")
|
||||
print(rootdir)
|
||||
print(fpath)
|
||||
with open(fpath,'w') as file:
|
||||
file.write(js)
|
||||
return fpath
|
||||
|
||||
@r.post("/test",)
|
||||
async def test(file:UploadFile= File(...),app:str=None):
|
||||
if file.filename.endswith('.xlsx'):
|
||||
@@ -233,15 +398,26 @@ async def test(file:UploadFile= File(...),app:str=None):
|
||||
# kintone = getfieldsfromkintone(app)
|
||||
# fields = analysefields(excel,kintone["properties"])
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}")
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
|
||||
return test
|
||||
|
||||
|
||||
@r.post("/upload",)
|
||||
async def upload(files:t.List[UploadFile] = File(...)):
|
||||
@r.post("/download",)
|
||||
async def download(request:Request,key,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||
try:
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
params = {"fileKey":key}
|
||||
url = f"{c.BASE_URL}/k/v1/file.json"
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
except Exception as e:
|
||||
raise APIException('kintone:upload',request.url._url,f"Error occurred while download file.json:",e)
|
||||
|
||||
@r.post("/upload")
|
||||
async def upload(request:Request,files:t.List[UploadFile] = File(...)):
|
||||
dataframes = []
|
||||
for file in files:
|
||||
if file.filename.endswith('.xlsx'):
|
||||
@@ -251,61 +427,103 @@ async def upload(files:t.List[UploadFile] = File(...)):
|
||||
print(df)
|
||||
dataframes.append(df)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
raise APIException('kintone:upload',request.url._url,f"Error occurred while uploading file {file.filename}:",e)
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
raise APIException('kintone:upload',request.url._url, f"File {file.filename} is not an Excel file",e)
|
||||
|
||||
return {"files": [file.filename for file in files]}
|
||||
|
||||
@r.get("/allapps",)
|
||||
async def allapps():
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/apps.json"
|
||||
r = httpx.get(url,headers=headers)
|
||||
return r.json()
|
||||
@r.post("/updatejscss")
|
||||
async def jscss(request:Request,app:str,files:t.List[UploadFile] = File(...),env = Depends(getkintoneenv)):
|
||||
try:
|
||||
jscs=[]
|
||||
for file in files:
|
||||
fbytes = file.file.read()
|
||||
fname = file.filename
|
||||
fpath = '{}\\{}'.format('Temp',fname)
|
||||
fout = open(fpath,'wb')
|
||||
fout.write(fbytes)
|
||||
fout.close()
|
||||
upload = uploadkintonefiles(fpath,env)
|
||||
if upload.get('fileKey') != None:
|
||||
jscs.append({ file.filename:upload['fileKey']})
|
||||
appjscs = updateappjscss(app,jscs,env)
|
||||
if appjscs.get("revision") != None:
|
||||
deoployappfromkintone(app,appjscs["revision"],env)
|
||||
return appjscs
|
||||
except Exception as e:
|
||||
raise APIException('kintone:updatejscss',request.url._url, f"Error occurred while update js/css {file.filename} is not an Excel file",e)
|
||||
|
||||
@r.get("/app")
|
||||
async def app(app:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/app.json"
|
||||
params ={"id":app}
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
async def app(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||
try:
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/app.json"
|
||||
params ={"id":app}
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
except Exception as e:
|
||||
raise APIException('kintone:app',request.url._url, f"Error occurred while get app({c.DOMAIN_NAM}->{app}):",e)
|
||||
|
||||
@r.get("/allapps")
|
||||
async def allapps(request:Request,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||
try:
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/apps.json"
|
||||
r = httpx.get(url,headers=headers)
|
||||
return r.json()
|
||||
except Exception as e:
|
||||
raise APIException('kintone:allapps',request.url._url, f"Error occurred while get allapps({c.DOMAIN_NAM}):",e)
|
||||
|
||||
@r.get("/appfields")
|
||||
async def appfields(app:str):
|
||||
return getfieldsfromkintone(app)
|
||||
async def appfields(request:Request,app:str,env = Depends(getkintoneenv)):
|
||||
try:
|
||||
return getfieldsfromkintone(app,env)
|
||||
except Exception as e:
|
||||
raise APIException('kintone:appfields',request.url._url, f"Error occurred while get app fileds({env.DOMAIN_NAM}->{app}):",e)
|
||||
|
||||
@r.get("/appprocess")
|
||||
async def appprocess(app:str):
|
||||
return getprocessfromkintone(app)
|
||||
async def appprocess(request:Request,app:str,env = Depends(getkintoneenv)):
|
||||
try:
|
||||
return getprocessfromkintone(app,env)
|
||||
except Exception as e:
|
||||
raise APIException('kintone:appprocess',request.url._url, f"Error occurred while get app process({env.DOMAIN_NAM}->{app}):",e)
|
||||
|
||||
@r.get("/alljscs")
|
||||
async def alljscs(app:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/app/customize.json"
|
||||
params = {"app":app}
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
@r.get("/alljscss")
|
||||
async def alljscs(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||
try:
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/app/customize.json"
|
||||
params = {"app":app}
|
||||
r = httpx.get(url,headers=headers,params=params)
|
||||
return r.json()
|
||||
except Exception as e:
|
||||
raise APIException('kintone:alljscss',request.url._url, f"Error occurred while get app js/css({c.DOMAIN_NAM}->{app}):",e)
|
||||
|
||||
@r.post("/createapp",)
|
||||
async def createapp(name:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
data = {"name":name}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app.json"
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
result = r.json()
|
||||
if result.get("app") != None:
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/deploy.json"
|
||||
data = {"apps":[result],"revert": False}
|
||||
async def createapp(request:Request,name:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||
try:
|
||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
data = {"name":name}
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app.json"
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
return r.json
|
||||
result = r.json()
|
||||
if result.get("app") != None:
|
||||
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/deploy.json"
|
||||
data = {"apps":[result],"revert": False}
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
return r.json
|
||||
except Exception as e:
|
||||
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({c.DOMAIN_NAM}->{name}):",e)
|
||||
|
||||
property=["label","code","type","required","defaultValue","options"]
|
||||
|
||||
@r.post("/createappfromexcel",)
|
||||
async def createappfromexcel(files:t.List[UploadFile] = File(...)):
|
||||
async def createappfromexcel(request:Request,files:t.List[UploadFile] = File(...),format:int = 0,env = Depends(getkintoneenv)):
|
||||
try:
|
||||
mapping = getkintoneformat()[format]
|
||||
except Exception as e:
|
||||
raise APIException('kintone:createappfromexcel',request.url._url, f"Error occurred while get kintone format:",e)
|
||||
|
||||
for file in files:
|
||||
if file.filename.endswith('.xlsx'):
|
||||
try:
|
||||
@@ -315,87 +533,90 @@ async def createappfromexcel(files:t.List[UploadFile] = File(...)):
|
||||
appname = df.iloc[0,2]
|
||||
desc = df.iloc[2,2]
|
||||
result = {"app":0,"revision":0,"msg":""}
|
||||
fields = getfieldsfromexcel(df)
|
||||
users = getkintoneusers()
|
||||
orgs = getkintoneorgs()
|
||||
fields = getfieldsfromexcel(df,mapping)
|
||||
users = getkintoneusers(env)
|
||||
orgs = getkintoneorgs(env)
|
||||
processes = getprocessfromexcel(df,users["users"], orgs["organizationTitles"])
|
||||
app = createkintoneapp(appname)
|
||||
app = createkintoneapp(appname,env)
|
||||
if app.get("app") != None:
|
||||
result["app"] = app["app"]
|
||||
app = updateappsettingstokintone(result["app"],{"description":desc})
|
||||
app = updateappsettingstokintone(result["app"],{"description":desc},env)
|
||||
if app.get("revision") != None:
|
||||
result["revision"] = app["revision"]
|
||||
app = addfieldstokintone(result["app"],fields)
|
||||
app = addfieldstokintone(result["app"],fields,env)
|
||||
if len(processes)> 0:
|
||||
app = updateprocesstokintone(result["app"],processes)
|
||||
app = updateprocesstokintone(result["app"],processes,env)
|
||||
if app.get("revision") != None:
|
||||
result["revision"] = app["revision"]
|
||||
deoployappfromkintone(result["app"],result["revision"])
|
||||
deoployappfromkintone(result["app"],result["revision"],env)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
raise APIException('kintone:createappfromexcel',request.url._url, f"Error occurred while parsing file ({env.DOMAIN_NAME}->{file.filename}):",e)
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
|
||||
raise APIException('kintone:createappfromexcel',request.url._url, f"File {file.filename} is not an Excel file",e)
|
||||
return result
|
||||
|
||||
|
||||
@r.post("/updateappfromexcel",)
|
||||
async def updateappfromexcel(app:str,files:t.List[UploadFile] = File(...)):
|
||||
@r.post("/updateappfromexcel")
|
||||
async def updateappfromexcel(request:Request,app:str,files:t.List[UploadFile] = File(...),format:int = 0,env = Depends(getkintoneenv)):
|
||||
try:
|
||||
mapping = getkintoneformat()[format]
|
||||
except Exception as e:
|
||||
raise APIException('kintone:updateappfromexcel',request.url._url, f"Error occurred while get kintone format:",e)
|
||||
|
||||
for file in files:
|
||||
if file.filename.endswith('.xlsx'):
|
||||
try:
|
||||
content = await file.read()
|
||||
df = pd.read_excel(BytesIO(content))
|
||||
excel = getsettingfromexcel(df)
|
||||
kintone= getsettingfromkintone(app)
|
||||
kintone= getsettingfromkintone(app,env)
|
||||
settings = analysesettings(excel,kintone)
|
||||
excel = getfieldsfromexcel(df)
|
||||
kintone = getfieldsfromkintone(app)
|
||||
users = getkintoneusers()
|
||||
orgs = getkintoneorgs()
|
||||
excel = getfieldsfromexcel(df,mapping)
|
||||
kintone = getfieldsfromkintone(app,env)
|
||||
users = getkintoneusers(env)
|
||||
orgs = getkintoneorgs(env)
|
||||
exp = getprocessfromexcel(df,users["users"], orgs["organizationTitles"])
|
||||
#exp = getprocessfromexcel(df)
|
||||
kinp = getprocessfromkintone(app)
|
||||
kinp = getprocessfromkintone(app,env)
|
||||
process = analysprocess(exp,kinp)
|
||||
revision = kintone["revision"]
|
||||
fields = analysefields(excel,kintone["properties"])
|
||||
result = {"app":app,"revision":revision,"msg":"No Update"}
|
||||
deploy = False
|
||||
if len(fields["update"]) > 0:
|
||||
result = updatefieldstokintone(app,revision,fields["update"])
|
||||
result = updatefieldstokintone(app,revision,fields["update"],env)
|
||||
revision = result["revision"]
|
||||
deploy = True
|
||||
if len(fields["add"]) > 0:
|
||||
result = addfieldstokintone(app,fields["add"],revision)
|
||||
result = addfieldstokintone(app,fields["add"],env,revision)
|
||||
revision = result["revision"]
|
||||
deploy = True
|
||||
if len(fields["del"]) > 0:
|
||||
result = deletefieldsfromkintone(app,revision,fields["del"])
|
||||
result = deletefieldsfromkintone(app,revision,fields["del"],env)
|
||||
revision = result["revision"]
|
||||
deploy = True
|
||||
if len(settings) > 0:
|
||||
result = updateappsettingstokintone(app,settings)
|
||||
result = updateappsettingstokintone(app,settings,env)
|
||||
revision = result["revision"]
|
||||
deploy = True
|
||||
if len(process)>0:
|
||||
result = updateprocesstokintone(app,exp)
|
||||
result = updateprocesstokintone(app,exp,env)
|
||||
revision = result["revision"]
|
||||
deploy = True
|
||||
if deploy:
|
||||
result = deoployappfromkintone(app,revision)
|
||||
result = deoployappfromkintone(app,revision,env)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
raise APIException('kintone:updateappfromexcel',request.url._url, f"Error occurred while parsing file ({env.DOMAIN_NAME}->{file.filename}):",e)
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
|
||||
raise APIException('kintone:updateappfromexcel',request.url._url, f"File {file.filename} is not an Excel file",e)
|
||||
return result
|
||||
|
||||
@r.post("/updateprocessfromexcel",)
|
||||
async def updateprocessfromexcel(app:str):
|
||||
async def updateprocessfromexcel(request:Request,app:str,env = Depends(getkintoneenv)):
|
||||
|
||||
try:
|
||||
excel = getprocessfromexcel()
|
||||
kintone = getprocessfromkintone(app)
|
||||
kintone = getprocessfromkintone(app,env)
|
||||
revision = kintone["revision"]
|
||||
#fields = analysefields(excel,kintone["properties"])
|
||||
result = {"app":app,"revision":revision,"msg":"No Update"}
|
||||
@@ -416,14 +637,31 @@ async def updateprocessfromexcel(app:str):
|
||||
# result = updateappsettingstokintone(app,settings)
|
||||
# revision = result["revision"]
|
||||
# deploy = True
|
||||
result = updateprocesstokintone(app,excel)
|
||||
result = updateprocesstokintone(app,excel,env)
|
||||
revision = result["revision"]
|
||||
deploy = True
|
||||
if deploy:
|
||||
result = deoployappfromkintone(app,revision)
|
||||
result = deoployappfromkintone(app,revision,env)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred : {str(e)}")
|
||||
raise APIException('kintone:updateprocessfromexcel',request.url._url, f"Error occurred while update process ({env.DOMAIN_NAM}->{app}):",e)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@r.post("/createjstokintone",)
|
||||
async def createjstokintone(request:Request,app:str,env:config.KINTONE_ENV = Depends(getkintoneenv)):
|
||||
try:
|
||||
jscs=[]
|
||||
files=[]
|
||||
files.append(createappjs(env.DOMAIN_ID, app))
|
||||
files.append('Temp\\alc_runtime.js')
|
||||
for file in files:
|
||||
upload = uploadkintonefiles(file,env)
|
||||
if upload.get('fileKey') != None:
|
||||
jscs.append({ file :upload['fileKey']})
|
||||
appjscs = updateappjscss(app,jscs,env)
|
||||
if appjscs.get("revision") != None:
|
||||
deoployappfromkintone(app,appjscs["revision"],env)
|
||||
return appjscs
|
||||
except Exception as e:
|
||||
raise APIException('kintone:createjstokintone',request.url._url, f"Error occurred while create js ({env.DOMAIN_NAM}->{app}):",e)
|
||||
|
||||
@@ -2,7 +2,10 @@ from fastapi import Request,Depends, APIRouter, UploadFile,HTTPException,File
|
||||
from app.db import Base,engine
|
||||
from app.db.session import get_db
|
||||
from app.db.crud import *
|
||||
from app.db.schemas import AppBase, AppEdit, App,Kintone
|
||||
from app.db.schemas import *
|
||||
from typing import List
|
||||
from app.core.auth import get_current_active_user,get_current_user
|
||||
from app.core.apiexception import APIException
|
||||
|
||||
platform_router = r = APIRouter()
|
||||
|
||||
@@ -16,9 +19,11 @@ async def appsetting_details(
|
||||
id: int,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
app = get_appsetting(db, id)
|
||||
return app
|
||||
|
||||
try:
|
||||
app = get_appsetting(db, id)
|
||||
return app
|
||||
except Exception as e:
|
||||
raise APIException('platform:appsettings',request.url._url,f"Error occurred while get app setting:",e)
|
||||
|
||||
@r.post("/appsettings", response_model=App, response_model_exclude_none=True)
|
||||
async def appsetting_create(
|
||||
@@ -26,7 +31,10 @@ async def appsetting_create(
|
||||
app: AppBase,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
return create_appsetting(db, app)
|
||||
try:
|
||||
return create_appsetting(db, app)
|
||||
except Exception as e:
|
||||
raise APIException('platform:appsettings',request.url._url,f"Error occurred while get create app setting:",e)
|
||||
|
||||
|
||||
@r.put(
|
||||
@@ -38,7 +46,10 @@ async def appsetting_edit(
|
||||
app: AppEdit,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
return edit_appsetting(db, id, app)
|
||||
try:
|
||||
return edit_appsetting(db, id, app)
|
||||
except Exception as e:
|
||||
raise APIException('platform:appsettings',request.url._url,f"Error occurred while edit app setting:",e)
|
||||
|
||||
|
||||
@r.delete(
|
||||
@@ -49,8 +60,10 @@ async def appsettings_delete(
|
||||
id: int,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
|
||||
return delete_appsetting(db, id)
|
||||
try:
|
||||
return delete_appsetting(db, id)
|
||||
except Exception as e:
|
||||
raise APIException('platform:appsettings',request.url._url,f"Error occurred while delete app setting:",e)
|
||||
|
||||
|
||||
@r.get(
|
||||
@@ -63,5 +76,270 @@ async def kintone_data(
|
||||
type: int,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
kintone = get_kintones(db, type)
|
||||
return kintone
|
||||
try:
|
||||
kintone = get_kintones(db, type)
|
||||
return kintone
|
||||
except Exception as e:
|
||||
raise APIException('platform:kintone',request.url._url,f"Error occurred while get kintone env:",e)
|
||||
|
||||
@r.get(
|
||||
"/actions",
|
||||
response_model=t.List[Action],
|
||||
response_model_exclude={"id"},
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def action_data(
|
||||
request: Request,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
actions = get_actions(db)
|
||||
return actions
|
||||
except Exception as e:
|
||||
raise APIException('platform:actions',request.url._url,f"Error occurred while get actions:",e)
|
||||
|
||||
@r.get(
|
||||
"/flow/{flowid}",
|
||||
response_model=Flow,
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def flow_details(
|
||||
request: Request,
|
||||
flowid: str,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
app = get_flow(db, flowid)
|
||||
return app
|
||||
except Exception as e:
|
||||
raise APIException('platform:flow',request.url._url,f"Error occurred while get flow by flowid:",e)
|
||||
|
||||
|
||||
@r.get(
|
||||
"/flows/{appid}",
|
||||
response_model=List[Flow],
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def flow_list(
|
||||
request: Request,
|
||||
appid: str,
|
||||
user=Depends(get_current_user),
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
domain = get_activedomain(db, user.id)
|
||||
print("domain=>",domain)
|
||||
flows = get_flows_by_app(db, domain.id, appid)
|
||||
return flows
|
||||
except Exception as e:
|
||||
raise APIException('platform:flow',request.url._url,f"Error occurred while get flow by appid:",e)
|
||||
|
||||
|
||||
@r.post("/flow", response_model=Flow, response_model_exclude_none=True)
|
||||
async def flow_create(
|
||||
request: Request,
|
||||
flow: FlowBase,
|
||||
user=Depends(get_current_user),
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
domain = get_activedomain(db, user.id)
|
||||
return create_flow(db, domain.id, flow)
|
||||
except Exception as e:
|
||||
raise APIException('platform:flow',request.url._url,f"Error occurred while create flow:",e)
|
||||
|
||||
|
||||
@r.put(
|
||||
"/flow/{flowid}", response_model=Flow, response_model_exclude_none=True
|
||||
)
|
||||
async def flow_edit(
|
||||
request: Request,
|
||||
flow: FlowBase,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
return edit_flow(db, flow)
|
||||
except Exception as e:
|
||||
raise APIException('platform:flow',request.url._url,f"Error occurred while edit flow:",e)
|
||||
|
||||
|
||||
@r.delete(
|
||||
"/flow/{flowid}", response_model=Flow, response_model_exclude_none=True
|
||||
)
|
||||
async def flow_delete(
|
||||
request: Request,
|
||||
flowid: str,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
return delete_flow(db, flowid)
|
||||
except Exception as e:
|
||||
raise APIException('platform:flow',request.url._url,f"Error occurred while delete flow:",e)
|
||||
|
||||
@r.get(
|
||||
"/domains/{tenantid}",
|
||||
response_model=List[Domain],
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def domain_details(
|
||||
request: Request,
|
||||
tenantid:str,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
domains = get_domains(db,tenantid)
|
||||
return domains
|
||||
except Exception as e:
|
||||
raise APIException('platform:domains',request.url._url,f"Error occurred while get domains:",e)
|
||||
|
||||
@r.post("/domain", response_model=Domain, response_model_exclude_none=True)
|
||||
async def domain_create(
|
||||
request: Request,
|
||||
domain: DomainBase,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
return create_domain(db, domain)
|
||||
except Exception as e:
|
||||
raise APIException('platform:domain',request.url._url,f"Error occurred while create domain:",e)
|
||||
|
||||
|
||||
@r.put(
|
||||
"/domain", response_model=Domain, response_model_exclude_none=True
|
||||
)
|
||||
async def domain_edit(
|
||||
request: Request,
|
||||
domain: DomainBase,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
return edit_domain(db, domain)
|
||||
except Exception as e:
|
||||
raise APIException('platform:domain',request.url._url,f"Error occurred while edit domain:",e)
|
||||
|
||||
|
||||
@r.delete(
|
||||
"/domain/{id}", response_model=Domain, response_model_exclude_none=True
|
||||
)
|
||||
async def domain_delete(
|
||||
request: Request,
|
||||
id: int,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
return delete_domain(db,id)
|
||||
except Exception as e:
|
||||
raise APIException('platform:domain',request.url._url,f"Error occurred while delete domain:",e)
|
||||
|
||||
@r.get(
|
||||
"/domain",
|
||||
response_model=List[Domain],
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def userdomain_details(
|
||||
request: Request,
|
||||
user=Depends(get_current_user),
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
domains = get_domain(db, user.id)
|
||||
return domains
|
||||
except Exception as e:
|
||||
raise APIException('platform:domain',request.url._url,f"Error occurred while get user({user.id}) domain:",e)
|
||||
|
||||
@r.post(
|
||||
"/domain/{userid}",
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def create_userdomain(
|
||||
request: Request,
|
||||
userid: int,
|
||||
domainids:list,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
domain = add_userdomain(db, userid,domainids)
|
||||
return domain
|
||||
except Exception as e:
|
||||
raise APIException('platform:domain',request.url._url,f"Error occurred while add user({userid}) domain:",e)
|
||||
|
||||
@r.delete(
|
||||
"/domain/{domainid}/{userid}", response_model_exclude_none=True
|
||||
)
|
||||
async def userdomain_delete(
|
||||
request: Request,
|
||||
domainid:int,
|
||||
userid: int,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
return delete_userdomain(db, userid,domainid)
|
||||
except Exception as e:
|
||||
raise APIException('platform:delete',request.url._url,f"Error occurred while delete user({userid}) domain:",e)
|
||||
|
||||
|
||||
@r.get(
|
||||
"/activedomain",
|
||||
response_model=Domain,
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def get_useractivedomain(
|
||||
request: Request,
|
||||
user=Depends(get_current_user),
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
domain = get_activedomain(db, user.id)
|
||||
return domain
|
||||
except Exception as e:
|
||||
raise APIException('platform:activedomain',request.url._url,f"Error occurred while get user({user.id}) activedomain:",e)
|
||||
|
||||
@r.put(
|
||||
"/activedomain/{domainid}",
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def update_activeuserdomain(
|
||||
request: Request,
|
||||
domainid:int,
|
||||
user=Depends(get_current_user),
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
domain = active_userdomain(db, user.id,domainid)
|
||||
return domain
|
||||
except Exception as e:
|
||||
raise APIException('platform:activedomain',request.url._url,f"Error occurred while update user({user.id}) activedomain:",e)
|
||||
|
||||
@r.get(
|
||||
"/events",
|
||||
response_model=t.List[Event],
|
||||
response_model_exclude={"id"},
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def event_data(
|
||||
request: Request,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
events = get_events(db)
|
||||
return events
|
||||
except Exception as e:
|
||||
raise APIException('platform:events',request.url._url,f"Error occurred while get events:",e)
|
||||
|
||||
|
||||
@r.get(
|
||||
"/eventactions/{eventid}",
|
||||
response_model=t.List[Action],
|
||||
response_model_exclude={"id"},
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def eventactions_data(
|
||||
request: Request,
|
||||
eventid: str,
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
actions = get_eventactions(db,eventid)
|
||||
return actions
|
||||
except Exception as e:
|
||||
raise APIException('platform:eventactions',request.url._url,f"Error occurred while get eventactions:",e)
|
||||
26
backend/app/core/apiexception.py
Normal file
26
backend/app/core/apiexception.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from fastapi import HTTPException, status
|
||||
from app.db.schemas import ErrorCreate
|
||||
from app.db.session import SessionLocal
|
||||
from app.db.crud import create_log
|
||||
|
||||
class APIException(Exception):
|
||||
|
||||
def __init__(self,location:str,title:str,content:str,e:Exception):
|
||||
if(str(e) == ''):
|
||||
content += e.detail
|
||||
self.detail = e.detail
|
||||
self.status_code = e.status_code
|
||||
else:
|
||||
self.detail = str(e)
|
||||
content += str(e)
|
||||
self.status_code = 500
|
||||
if(len(content) > 5000):
|
||||
content =content[0:5000]
|
||||
self.error = ErrorCreate(location=location,title=title,content=content)
|
||||
|
||||
def writedblog(exc: APIException):
|
||||
db = SessionLocal()
|
||||
try:
|
||||
create_log(db,exc.error)
|
||||
finally:
|
||||
db.close()
|
||||
@@ -3,7 +3,7 @@ from fastapi import Depends, HTTPException, status
|
||||
from jwt import PyJWTError
|
||||
|
||||
from app.db import models, schemas, session
|
||||
from app.db.crud import get_user_by_email, create_user
|
||||
from app.db.crud import get_user_by_email, create_user,get_user
|
||||
from app.core import security
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@ async def get_current_user(
|
||||
payload = jwt.decode(
|
||||
token, security.SECRET_KEY, algorithms=[security.ALGORITHM]
|
||||
)
|
||||
email: str = payload.get("sub")
|
||||
if email is None:
|
||||
id: int = payload.get("sub")
|
||||
if id is None:
|
||||
raise credentials_exception
|
||||
permissions: str = payload.get("permissions")
|
||||
token_data = schemas.TokenData(email=email, permissions=permissions)
|
||||
token_data = schemas.TokenData(id = id, permissions=permissions)
|
||||
except PyJWTError:
|
||||
raise credentials_exception
|
||||
user = get_user_by_email(db, token_data.email)
|
||||
user = get_user(db, token_data.id)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
@@ -58,7 +58,7 @@ def authenticate_user(db, email: str, password: str):
|
||||
return user
|
||||
|
||||
|
||||
def sign_up_new_user(db, email: str, password: str):
|
||||
def sign_up_new_user(db, email: str, password: str, firstname: str,lastname: str):
|
||||
user = get_user_by_email(db, email)
|
||||
if user:
|
||||
return False # User already exists
|
||||
@@ -67,6 +67,8 @@ def sign_up_new_user(db, email: str, password: str):
|
||||
schemas.UserCreate(
|
||||
email=email,
|
||||
password=password,
|
||||
first_name = firstname,
|
||||
last_name = lastname,
|
||||
is_active=True,
|
||||
is_superuser=False,
|
||||
),
|
||||
|
||||
@@ -1,18 +1,39 @@
|
||||
import os
|
||||
import base64
|
||||
|
||||
PROJECT_NAME = "KintoneAppBuilder"
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = "mssql+pymssql://maxz64@maxzdb:m@xz1205@maxzdb.database.windows.net/alloc"
|
||||
|
||||
BASE_URL = "https://mfu07rkgnb7c.cybozu.com"
|
||||
#SQLALCHEMY_DATABASE_URI = "postgres://maxz64:m@xz1205@alicornkintone.postgres.database.azure.com/postgres"
|
||||
SQLALCHEMY_DATABASE_URI = "postgres://kabAdmin:P@ssw0rd!@kintonetooldb.postgres.database.azure.com/postgres"
|
||||
|
||||
API_V1_STR = "/k/v1"
|
||||
|
||||
API_V1_AUTH_KEY = "X-Cybozu-Authorization"
|
||||
|
||||
API_V1_AUTH_VALUE = "TVhaOm1heHoxMjA1"
|
||||
DEPLOY_MODE = "DEV" #DEV,PROD
|
||||
|
||||
KINTONE_USER = "MXZ"
|
||||
DEPLOY_JS_URL = "https://ka-addin.azurewebsites.net/alc_runtime.js"
|
||||
#DEPLOY_JS_URL = "https://92dc-133-139-109-57.ngrok-free.app/alc_runtime.js"
|
||||
|
||||
KINTONE_FIELD_TYPE=["GROUP","GROUP_SELECT","CHECK_BOX","SUBTABLE","DROP_DOWN","USER_SELECT","RADIO_BUTTON","RICH_TEXT","LINK","REFERENCE_TABLE","CALC","TIME","NUMBER","ORGANIZATION_SELECT","FILE","DATETIME","DATE","MULTI_SELECT","SINGLE_LINE_TEXT","MULTI_LINE_TEXT"]
|
||||
|
||||
KINTONE_FIELD_PROPERTY=['label','code','type','required','unique','maxValue','minValue','maxLength','minLength','defaultValue','defaultNowValue','options','expression','hideExpression','digit','protocol','displayScale','unit','unitPosition']
|
||||
|
||||
class KINTONE_ENV:
|
||||
|
||||
BASE_URL = ""
|
||||
|
||||
API_V1_AUTH_VALUE = ""
|
||||
|
||||
KINTONE_USER = ""
|
||||
|
||||
DOMAIN_ID = ""
|
||||
|
||||
DOMAIN_NAME =""
|
||||
|
||||
def __init__(self,domain) -> None:
|
||||
self.DOMAIN_NAME=domain.name
|
||||
self.DOMAIN_ID=domain.id
|
||||
self.BASE_URL = domain.url
|
||||
self.KINTONE_USER = domain.kintoneuser
|
||||
self.API_V1_AUTH_VALUE = base64.b64encode(bytes(f"{domain.kintoneuser}:{domain.kintonepwd}","utf-8"))
|
||||
@@ -9,7 +9,7 @@ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
SECRET_KEY = "alicorns"
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 2880
|
||||
|
||||
|
||||
def get_password_hash(password: str) -> str:
|
||||
@@ -25,7 +25,7 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import and_
|
||||
import typing as t
|
||||
|
||||
from . import models, schemas
|
||||
@@ -116,3 +117,182 @@ def get_kintones(db: Session, type: int):
|
||||
if not kintones:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return kintones
|
||||
|
||||
def get_actions(db: Session):
|
||||
actions = db.query(models.Action).all()
|
||||
if not actions:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return actions
|
||||
|
||||
|
||||
def create_flow(db: Session, domainid: int, flow: schemas.FlowBase):
|
||||
db_flow = models.Flow(
|
||||
flowid=flow.flowid,
|
||||
appid=flow.appid,
|
||||
eventid=flow.eventid,
|
||||
domainid=domainid,
|
||||
name=flow.name,
|
||||
content=flow.content
|
||||
)
|
||||
db.add(db_flow)
|
||||
db.commit()
|
||||
db.refresh(db_flow)
|
||||
return db_flow
|
||||
|
||||
def delete_flow(db: Session, flowid: str):
|
||||
flow = get_flow(db, flowid)
|
||||
if not flow:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Flow not found")
|
||||
db.delete(flow)
|
||||
db.commit()
|
||||
return flow
|
||||
|
||||
|
||||
def edit_flow(
|
||||
db: Session, flow: schemas.FlowBase
|
||||
) -> schemas.Flow:
|
||||
db_flow = get_flow(db, flow.flowid)
|
||||
if not db_flow:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Flow not found")
|
||||
update_data = flow.dict(exclude_unset=True)
|
||||
|
||||
for key, value in update_data.items():
|
||||
setattr(db_flow, key, value)
|
||||
|
||||
db.add(db_flow)
|
||||
db.commit()
|
||||
db.refresh(db_flow)
|
||||
return db_flow
|
||||
|
||||
|
||||
def get_flows(db: Session, flowid: str):
|
||||
flows = db.query(models.Flow).all()
|
||||
if not flows:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return flows
|
||||
|
||||
def get_flow(db: Session, flowid: str):
|
||||
flow = db.query(models.Flow).filter(models.Flow.flowid == flowid).first()
|
||||
if not flow:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return flow
|
||||
|
||||
def get_flows_by_app(db: Session, domainid: int, appid: str):
|
||||
flows = db.query(models.Flow).filter(and_(models.Flow.domainid == domainid,models.Flow.appid == appid)).all()
|
||||
if not flows:
|
||||
raise Exception("Data not found")
|
||||
return flows
|
||||
|
||||
def create_domain(db: Session, domain: schemas.DomainBase):
|
||||
db_domain = models.Domain(
|
||||
tenantid = domain.tenantid,
|
||||
name=domain.name,
|
||||
url=domain.url,
|
||||
kintoneuser=domain.kintoneuser,
|
||||
kintonepwd=domain.kintonepwd
|
||||
)
|
||||
db.add(db_domain)
|
||||
db.commit()
|
||||
db.refresh(db_domain)
|
||||
return db_domain
|
||||
|
||||
def delete_domain(db: Session,id: int):
|
||||
db_domain = db.query(models.Domain).get(id)
|
||||
if not db_domain:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Domain not found")
|
||||
db.delete(db_domain)
|
||||
db.commit()
|
||||
return db_domain
|
||||
|
||||
|
||||
def edit_domain(
|
||||
db: Session, domain: schemas.DomainBase
|
||||
) -> schemas.Domain:
|
||||
db_domain = db.query(models.Domain).get(domain.id)
|
||||
if not db_domain:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Domain not found")
|
||||
update_data = domain.dict(exclude_unset=True)
|
||||
|
||||
for key, value in update_data.items():
|
||||
if(key != "id"):
|
||||
setattr(db_domain, key, value)
|
||||
|
||||
db.add(db_domain)
|
||||
db.commit()
|
||||
db.refresh(db_domain)
|
||||
return db_domain
|
||||
|
||||
def add_userdomain(db: Session, userid:int,domainids:list):
|
||||
for domainid in domainids:
|
||||
db_domain = models.UserDomain(
|
||||
userid = userid,
|
||||
domainid = domainid
|
||||
)
|
||||
db.add(db_domain)
|
||||
db.commit()
|
||||
db.refresh(db_domain)
|
||||
return db_domain
|
||||
|
||||
def delete_userdomain(db: Session, userid: int,domainid: int):
|
||||
db_domain = db.query(models.UserDomain).filter(and_(models.UserDomain.userid == userid,models.UserDomain.domainid == domainid)).first()
|
||||
if not db_domain:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Domain not found")
|
||||
db.delete(db_domain)
|
||||
db.commit()
|
||||
return db_domain
|
||||
|
||||
def active_userdomain(db: Session, userid: int,domainid: int):
|
||||
db_userdomains = db.query(models.UserDomain).filter(models.UserDomain.userid == userid).all()
|
||||
if not db_userdomains:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Domain not found")
|
||||
for domain in db_userdomains:
|
||||
if domain.domainid == domainid:
|
||||
domain.active = True
|
||||
else:
|
||||
domain.active = False
|
||||
db.add(domain)
|
||||
db.commit()
|
||||
return db_userdomains
|
||||
|
||||
def get_activedomain(db: Session, userid: int):
|
||||
db_domain = db.query(models.Domain).join(models.UserDomain,models.UserDomain.domainid == models.Domain.id ).filter(and_(models.UserDomain.userid == userid,models.UserDomain.active == True)).first()
|
||||
if not db_domain:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Domain not found")
|
||||
return db_domain
|
||||
|
||||
def get_domain(db: Session, userid: str):
|
||||
domains = db.query(models.Domain).join(models.UserDomain,models.UserDomain.domainid == models.Domain.id ).filter(models.UserDomain.userid == userid).all()
|
||||
if not domains:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return domains
|
||||
|
||||
def get_domains(db: Session,tenantid:str):
|
||||
domains = db.query(models.Domain).filter(models.Domain.tenantid == tenantid ).all()
|
||||
if not domains:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return domains
|
||||
|
||||
def get_events(db: Session):
|
||||
events = db.query(models.Event).all()
|
||||
if not events:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return events
|
||||
|
||||
def get_eventactions(db: Session,eventid: str):
|
||||
#eveactions = db.query(models.Action).join(models.EventAction,models.EventAction.actionid == models.Action.id ).join(models.Event,models.Event.id == models.EventAction.eventid).filter(models.Event.eventid == eventid).all()
|
||||
eveactions = db.query(models.Action).join(models.EventAction,models.EventAction.actionid != models.Action.id and models.EventAction.eventid == eventid ).join(models.Event,models.Event.id == models.EventAction.eventid).filter(models.Event.eventid == eventid).all()
|
||||
if not eveactions:
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return eveactions
|
||||
|
||||
|
||||
def create_log(db: Session, error:schemas.ErrorCreate):
|
||||
db_log = models.ErrorLog(title=error.title,location=error.location,content=error.content)
|
||||
db.add(db_log)
|
||||
db.commit()
|
||||
db.refresh(db_log)
|
||||
return db_log
|
||||
|
||||
def get_kintoneformat(db: Session):
|
||||
formats = db.query(models.KintoneFormat).order_by(models.KintoneFormat.id).all()
|
||||
return formats
|
||||
@@ -1,12 +1,16 @@
|
||||
from sqlalchemy import Boolean, Column, Integer, String
|
||||
|
||||
from .session import Base
|
||||
from sqlalchemy import Boolean, Column, Integer, String, DateTime,ForeignKey
|
||||
from sqlalchemy.ext.declarative import as_declarative
|
||||
from datetime import datetime
|
||||
|
||||
@as_declarative()
|
||||
class Base:
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
create_time = Column(DateTime, default=datetime.now)
|
||||
update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "user"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String(50), unique=True, index=True, nullable=False)
|
||||
first_name = Column(String(100))
|
||||
last_name = Column(String(100))
|
||||
@@ -17,15 +21,93 @@ class User(Base):
|
||||
class AppSetting(Base):
|
||||
__tablename__ = "appsetting"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
appid = Column(String(100), index=True, nullable=False)
|
||||
setting = Column(String(1000))
|
||||
|
||||
class Kintone(Base):
|
||||
__tablename__ = "kintone"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
type = Column(Integer, index=True, nullable=False)
|
||||
name = Column(String(100), nullable=False)
|
||||
desc = Column(String)
|
||||
content = Column(String)
|
||||
|
||||
class Action(Base):
|
||||
__tablename__ = "action"
|
||||
|
||||
name = Column(String(100), index=True, nullable=False)
|
||||
title = Column(String(200))
|
||||
subtitle = Column(String(500))
|
||||
outputpoints = Column(String)
|
||||
property = Column(String)
|
||||
|
||||
class Flow(Base):
|
||||
__tablename__ = "flow"
|
||||
|
||||
flowid = Column(String(100), index=True, nullable=False)
|
||||
appid = Column(String(100), index=True, nullable=False)
|
||||
eventid = Column(String(100), index=True, nullable=False)
|
||||
domainid = Column(Integer,ForeignKey("domain.id"))
|
||||
name = Column(String(200))
|
||||
content = Column(String)
|
||||
|
||||
class Tenant(Base):
|
||||
__tablename__ = "tenant"
|
||||
|
||||
tenantid = Column(String(100), index=True, nullable=False)
|
||||
name = Column(String(200))
|
||||
licence = Column(String(200))
|
||||
startdate = Column(DateTime)
|
||||
enddate = Column(DateTime)
|
||||
|
||||
class Domain(Base):
|
||||
__tablename__ = "domain"
|
||||
|
||||
tenantid = Column(String(100), index=True, nullable=False)
|
||||
name = Column(String(100), nullable=False)
|
||||
url = Column(String(200), nullable=False)
|
||||
kintoneuser = Column(String(100), nullable=False)
|
||||
kintonepwd = Column(String(100), nullable=False)
|
||||
|
||||
|
||||
class UserDomain(Base):
|
||||
__tablename__ = "userdomain"
|
||||
|
||||
userid = Column(Integer,ForeignKey("user.id"))
|
||||
domainid = Column(Integer,ForeignKey("domain.id"))
|
||||
active = Column(Boolean, default=False)
|
||||
|
||||
class Event(Base):
|
||||
__tablename__ = "event"
|
||||
|
||||
category = Column(String(100), nullable=False)
|
||||
type = Column(String(100), nullable=False)
|
||||
eventid= Column(String(100), nullable=False)
|
||||
function = Column(String(500), nullable=False)
|
||||
mobile = Column(Boolean, default=False)
|
||||
eventgroup = Column(Boolean, default=False)
|
||||
|
||||
class EventAction(Base):
|
||||
__tablename__ = "eventaction"
|
||||
|
||||
eventid = Column(Integer,ForeignKey("event.id"))
|
||||
actionid = Column(Integer,ForeignKey("action.id"))
|
||||
|
||||
|
||||
class ErrorLog(Base):
|
||||
__tablename__ = "errorlog"
|
||||
|
||||
title = Column(String(50))
|
||||
location = Column(String(500))
|
||||
content = Column(String(5000))
|
||||
|
||||
class KintoneFormat(Base):
|
||||
__tablename__ = "kintoneformat"
|
||||
|
||||
name = Column(String(50))
|
||||
startrow =Column(Integer)
|
||||
startcolumn =Column(Integer)
|
||||
typecolumn =Column(Integer)
|
||||
codecolumn =Column(Integer)
|
||||
field = Column(String(5000))
|
||||
trueformat = Column(String(10))
|
||||
@@ -1,7 +1,12 @@
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
import typing as t
|
||||
|
||||
|
||||
class Base(BaseModel):
|
||||
create_time: datetime
|
||||
update_time: datetime
|
||||
|
||||
class UserBase(BaseModel):
|
||||
email: str
|
||||
is_active: bool = True
|
||||
@@ -15,7 +20,12 @@ class UserOut(UserBase):
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
email:str
|
||||
password: str
|
||||
first_name: str
|
||||
last_name: str
|
||||
is_active:bool
|
||||
is_superuser:bool
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
@@ -41,6 +51,7 @@ class Token(BaseModel):
|
||||
|
||||
|
||||
class TokenData(BaseModel):
|
||||
id:int = 0
|
||||
email: str = None
|
||||
permissions: str = "user"
|
||||
|
||||
@@ -69,3 +80,69 @@ class Kintone(BaseModel):
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
class Action(BaseModel):
|
||||
id: int
|
||||
name: str = None
|
||||
title: str = None
|
||||
subtitle: str = None
|
||||
outputpoints: str = None
|
||||
property: str = None
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
class FlowBase(BaseModel):
|
||||
flowid: str
|
||||
appid: str
|
||||
eventid: str
|
||||
name: str = None
|
||||
content: str = None
|
||||
|
||||
class Flow(Base):
|
||||
id: int
|
||||
flowid: str
|
||||
appid: str
|
||||
eventid: str
|
||||
domainid: int
|
||||
name: str = None
|
||||
content: str = None
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
class DomainBase(BaseModel):
|
||||
id: int
|
||||
tenantid: str
|
||||
name: str
|
||||
url: str
|
||||
kintoneuser: str
|
||||
kintonepwd: str
|
||||
|
||||
class Domain(Base):
|
||||
id: int
|
||||
tenantid: str
|
||||
name: str
|
||||
url: str
|
||||
kintoneuser: str
|
||||
kintonepwd: str
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
class Event(Base):
|
||||
id: int
|
||||
category: str
|
||||
type: str
|
||||
eventid: str
|
||||
function: str
|
||||
mobile: bool
|
||||
eventgroup: bool
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
class ErrorCreate(BaseModel):
|
||||
title:str
|
||||
location:str
|
||||
content:str
|
||||
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from fastapi import FastAPI, Depends
|
||||
from starlette.requests import Request
|
||||
import uvicorn
|
||||
@@ -11,6 +12,11 @@ from app.core.auth import get_current_active_user
|
||||
from app.core.celery_app import celery_app
|
||||
from app import tasks
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import logging
|
||||
from app.core.apiexception import APIException, writedblog
|
||||
from app.db.crud import create_log
|
||||
from fastapi.responses import JSONResponse
|
||||
import asyncio
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
@@ -19,9 +25,7 @@ app = FastAPI(
|
||||
)
|
||||
|
||||
origins = [
|
||||
"http://localhost:9000",
|
||||
"http://localhost",
|
||||
"http://localhost:8080",
|
||||
"*"
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
@@ -39,6 +43,25 @@ app.add_middleware(
|
||||
# request.state.db.close()
|
||||
# return response
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
log_dir="log"
|
||||
if not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
|
||||
logger = logging.getLogger("uvicorn.access")
|
||||
handler = logging.handlers.RotatingFileHandler(f"{log_dir}/api.log",mode="a",maxBytes = 100*1024, backupCount = 3)
|
||||
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
|
||||
logger.addHandler(handler)
|
||||
|
||||
@app.exception_handler(APIException)
|
||||
async def api_exception_handler(request: Request, exc: APIException):
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_in_executor(None,writedblog,exc)
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={"detail": f"{exc.detail}"},
|
||||
)
|
||||
|
||||
@app.get("/api/v1")
|
||||
async def root():
|
||||
|
||||
@@ -21,3 +21,4 @@ pandas==2.0.3
|
||||
openpyxl==3.1.2
|
||||
deepdiff==6.3.1
|
||||
pymssql==2.2.7
|
||||
psycopg2==2.9.8
|
||||
845
document/ALCKintone_20231012.drawio
Normal file
845
document/ALCKintone_20231012.drawio
Normal file
@@ -0,0 +1,845 @@
|
||||
<mxfile host="65bd71144e" pages="3">
|
||||
<diagram name="Page-1" id="efa7a0a1-bf9b-a30e-e6df-94a7791c09e9">
|
||||
<mxGraphModel dx="1073" dy="518" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="826" pageHeight="1169" background="none" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-114" value="<p style="margin: 0px; margin-top: 4px; text-align: center; text-decoration: underline;"><strong>User</strong></p><p style="margin: 0px; margin-left: 8px;"><br></p><p style="margin: 0px; margin-left: 8px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="21.64" width="160" height="78.36" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-116" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" target="ZIlfFuTIaODnUzWRKW0w-114" edge="1">
|
||||
<mxGeometry x="389.35999999999996" y="350" as="geometry">
|
||||
<mxPoint x="350" y="60.820000000000164" as="sourcePoint"/>
|
||||
<mxPoint x="671" y="532" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-117" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-116" connectable="0" vertex="1">
|
||||
<mxGeometry x="-1" relative="1" as="geometry">
|
||||
<mxPoint x="-30" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-118" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-116" connectable="0" vertex="1">
|
||||
<mxGeometry x="1" relative="1" as="geometry">
|
||||
<mxPoint x="18.919999999999582" y="-21.960000000000644" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-119" value="<p style="margin: 0px; margin-top: 4px; text-align: center; text-decoration: underline;"><strong>UserApp</strong></p><hr><p style="margin: 0px; margin-left: 8px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" vertex="1">
|
||||
<mxGeometry x="350" y="250" width="160" height="78.36" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-120" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" source="ZIlfFuTIaODnUzWRKW0w-119" edge="1">
|
||||
<mxGeometry x="399.35999999999996" y="360" as="geometry">
|
||||
<mxPoint x="360" y="71" as="sourcePoint"/>
|
||||
<mxPoint x="430" y="100.00000000000023" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="430" y="140"/>
|
||||
<mxPoint x="430" y="140"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-121" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-120" connectable="0" vertex="1">
|
||||
<mxGeometry x="-1" relative="1" as="geometry">
|
||||
<mxPoint x="9" y="-20" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-122" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-120" connectable="0" vertex="1">
|
||||
<mxGeometry x="1" relative="1" as="geometry">
|
||||
<mxPoint x="18.919999999999582" y="-21.960000000000644" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-123" value="<p style="margin: 0px; margin-top: 4px; text-align: center; text-decoration: underline;"><strong>UserDomain</strong></p><hr><p style="margin: 0px; margin-left: 8px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" vertex="1">
|
||||
<mxGeometry x="350" y="21.64" width="160" height="78.36" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-124" value="<p style="margin: 0px; margin-top: 4px; text-align: center; text-decoration: underline;"><b>Action</b></p><hr><p style="margin: 0px; margin-left: 8px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" vertex="1">
|
||||
<mxGeometry x="30" y="440" width="160" height="78.36" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-125" value="<p style="margin: 0px; margin-top: 4px; text-align: center; text-decoration: underline;"><strong>AppAction</strong></p><hr><p style="margin: 0px; margin-left: 8px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" vertex="1">
|
||||
<mxGeometry x="350" y="440" width="160" height="78.36" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-130" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="ZIlfFuTIaODnUzWRKW0w-125" target="ZIlfFuTIaODnUzWRKW0w-124" edge="1">
|
||||
<mxGeometry x="209.35999999999999" y="733" as="geometry">
|
||||
<mxPoint x="240.59" y="584.64" as="sourcePoint"/>
|
||||
<mxPoint x="240.00000000000003" y="473.0000000000002" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="241" y="479"/>
|
||||
<mxPoint x="200" y="480"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-131" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-130" connectable="0" vertex="1">
|
||||
<mxGeometry x="-1" relative="1" as="geometry">
|
||||
<mxPoint x="-30" y="-29" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-132" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-130" connectable="0" vertex="1">
|
||||
<mxGeometry x="1" relative="1" as="geometry">
|
||||
<mxPoint x="18.919999999999582" y="-21.960000000000644" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-133" value="<p style="margin: 0px; margin-top: 4px; text-align: center; text-decoration: underline;"><b>Flow</b></p><hr><p style="margin: 0px; margin-left: 8px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" vertex="1">
|
||||
<mxGeometry x="630" y="250" width="160" height="78.36" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-134" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="ZIlfFuTIaODnUzWRKW0w-133" target="ZIlfFuTIaODnUzWRKW0w-119" edge="1">
|
||||
<mxGeometry x="408.77" y="598.36" as="geometry">
|
||||
<mxPoint x="440" y="450" as="sourcePoint"/>
|
||||
<mxPoint x="439.41" y="338.36000000000024" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="570" y="290"/>
|
||||
<mxPoint x="570" y="290"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-135" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-134" connectable="0" vertex="1">
|
||||
<mxGeometry x="-1" relative="1" as="geometry">
|
||||
<mxPoint x="-30" y="-20" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-136" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="ZIlfFuTIaODnUzWRKW0w-134" connectable="0" vertex="1">
|
||||
<mxGeometry x="1" relative="1" as="geometry">
|
||||
<mxPoint x="18.919999999999582" y="-21.960000000000644" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="ZIlfFuTIaODnUzWRKW0w-140" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" connectable="0" vertex="1">
|
||||
<mxGeometry x="420.00000020761206" y="99.99999999998707" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="Z9b4y0bF-qkiY6ReSsG3-1" value="<p style="margin: 0px; margin-top: 4px; text-align: center; text-decoration: underline;"><b>Tenant</b></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="1" vertex="1">
|
||||
<mxGeometry x="40" y="250" width="160" height="78.36" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="Z9b4y0bF-qkiY6ReSsG3-2" value="" style="endArrow=open;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="ZIlfFuTIaODnUzWRKW0w-114" target="Z9b4y0bF-qkiY6ReSsG3-1" edge="1">
|
||||
<mxGeometry x="209.35999999999999" y="733" as="geometry">
|
||||
<mxPoint x="230" y="220" as="sourcePoint"/>
|
||||
<mxPoint x="70" y="220" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="120" y="220"/>
|
||||
<mxPoint x="121" y="220"/>
|
||||
<mxPoint x="110" y="221"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Z9b4y0bF-qkiY6ReSsG3-3" value="0..n" style="resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="Z9b4y0bF-qkiY6ReSsG3-2" connectable="0" vertex="1">
|
||||
<mxGeometry x="-1" relative="1" as="geometry">
|
||||
<mxPoint x="450" y="-100" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Z9b4y0bF-qkiY6ReSsG3-4" value="1" style="resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;strokeColor=#003366;shadow=1;fillColor=#D4E1F5;fontColor=#003366" parent="Z9b4y0bF-qkiY6ReSsG3-2" connectable="0" vertex="1">
|
||||
<mxGeometry x="1" relative="1" as="geometry">
|
||||
<mxPoint x="18.919999999999582" y="-21.960000000000644" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
<diagram id="lCZzTbn_7m8qK95pbvvS" name="ER図">
|
||||
<mxGraphModel dx="1900" dy="518" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-1" value="User" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
|
||||
<mxGeometry x="420" y="20" width="180" height="180" as="geometry">
|
||||
<mxRectangle x="310" y="60" width="110" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-2" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="inIyfaXWTM6ArMeTnGn6-1" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-3" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-2" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-4" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-2" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-5" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-1" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-6" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-5" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-7" value="<b style="border-color: var(--border-color); text-align: center; color: rgb(0, 51, 102);">Tenant_id</b>" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-5" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-8" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-1" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-9" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-8" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-10" value="ユーザー名" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-8" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-11" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-1" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-12" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-11" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-13" value="パスワード(暗号化)" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-11" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-1" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-1" vertex="1">
|
||||
<mxGeometry y="150" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-2" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="NCbWPNvujZOKFJAbQVH6-1" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-3" value="その他情報" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="NCbWPNvujZOKFJAbQVH6-1" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-14" value="Domain" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
|
||||
<mxGeometry x="420" y="240" width="180" height="180" as="geometry">
|
||||
<mxRectangle x="590" y="60" width="100" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-15" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="inIyfaXWTM6ArMeTnGn6-14" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-16" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-15" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-17" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-15" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-24" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-14" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-25" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-24" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="inIyfaXWTM6ArMeTnGn6-26" value="domain_url" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="inIyfaXWTM6ArMeTnGn6-24" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-31" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-14" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-32" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="GH2g80-cBXHe62XdPV4N-31" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-33" value="kintoneユーザーID" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="GH2g80-cBXHe62XdPV4N-31" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-34" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-14" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-35" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="GH2g80-cBXHe62XdPV4N-34" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-36" value="kintoneパスワード" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="GH2g80-cBXHe62XdPV4N-34" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-4" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="inIyfaXWTM6ArMeTnGn6-14" vertex="1">
|
||||
<mxGeometry y="150" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-5" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="NCbWPNvujZOKFJAbQVH6-4" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-6" value="API_Key" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="NCbWPNvujZOKFJAbQVH6-4" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-1" value="<b style="border-color: var(--border-color); color: rgb(0, 51, 102);">Tenant</b>" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
|
||||
<mxGeometry x="130" y="20" width="180" height="180" as="geometry">
|
||||
<mxRectangle x="40" y="60" width="100" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-2" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="QUGX19b9cPh8sdE7zjoR-1" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-3" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-2" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-4" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-2" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-5" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="QUGX19b9cPh8sdE7zjoR-1" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-6" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-5" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-7" value="名前" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-5" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-8" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="QUGX19b9cPh8sdE7zjoR-1" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-9" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-8" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-10" value="ライセンスキー" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-8" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-11" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="QUGX19b9cPh8sdE7zjoR-1" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-12" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-11" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="QUGX19b9cPh8sdE7zjoR-13" value="利用期限" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="QUGX19b9cPh8sdE7zjoR-11" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="xY3sbCmQmCiIU-0kb39k-5" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="QUGX19b9cPh8sdE7zjoR-1" vertex="1">
|
||||
<mxGeometry y="150" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="xY3sbCmQmCiIU-0kb39k-6" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="xY3sbCmQmCiIU-0kb39k-5" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="xY3sbCmQmCiIU-0kb39k-7" value="その他情報" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="xY3sbCmQmCiIU-0kb39k-5" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-1" value="Flow" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="420" y="480" width="180" height="210" as="geometry">
|
||||
<mxRectangle x="320" y="370" width="60" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-2" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="8Zu1yShcSHxMs68hy39H-1" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-3" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="8Zu1yShcSHxMs68hy39H-2" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-4" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="8Zu1yShcSHxMs68hy39H-2" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-56" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="8Zu1yShcSHxMs68hy39H-1" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-57" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=0;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-56" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-58" value="domain_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=0;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-56" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-5" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="8Zu1yShcSHxMs68hy39H-1" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-6" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="8Zu1yShcSHxMs68hy39H-5" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-7" value="app_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="8Zu1yShcSHxMs68hy39H-5" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-8" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="8Zu1yShcSHxMs68hy39H-1" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-9" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="8Zu1yShcSHxMs68hy39H-8" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="8Zu1yShcSHxMs68hy39H-10" value="spaceid" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="8Zu1yShcSHxMs68hy39H-8" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="XZEu5LJmFrV2HCpzu9aW-12" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="8Zu1yShcSHxMs68hy39H-1" vertex="1">
|
||||
<mxGeometry y="150" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="XZEu5LJmFrV2HCpzu9aW-13" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="XZEu5LJmFrV2HCpzu9aW-12" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="XZEu5LJmFrV2HCpzu9aW-14" value="content(json)" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="XZEu5LJmFrV2HCpzu9aW-12" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-14" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="8Zu1yShcSHxMs68hy39H-1" vertex="1">
|
||||
<mxGeometry y="180" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-15" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="GH2g80-cBXHe62XdPV4N-14" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-16" value="event_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="GH2g80-cBXHe62XdPV4N-14" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-1" value="Event" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
|
||||
<mxGeometry x="130" y="480" width="180" height="150" as="geometry">
|
||||
<mxRectangle x="40" y="390" width="70" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-2" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="GH2g80-cBXHe62XdPV4N-1" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-3" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-2" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-4" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-2" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-5" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="GH2g80-cBXHe62XdPV4N-1" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-6" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-5" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-7" value="画面名" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-5" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-8" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="GH2g80-cBXHe62XdPV4N-1" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-9" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-8" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-10" value="イベント名" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-8" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-11" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="GH2g80-cBXHe62XdPV4N-1" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-12" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-11" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-13" value="モバイル使用可能か" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-11" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-17" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERoneToMany;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=1;" parent="1" source="GH2g80-cBXHe62XdPV4N-2" target="GH2g80-cBXHe62XdPV4N-14" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="630" y="845" as="sourcePoint"/>
|
||||
<mxPoint x="530" y="1085" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-18" value="App" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="280" width="180" height="150" as="geometry">
|
||||
<mxRectangle x="600" y="370" width="60" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-34" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="GH2g80-cBXHe62XdPV4N-18" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-35" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-34" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-36" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-34" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-51" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="GH2g80-cBXHe62XdPV4N-18" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-52" value="UK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-51" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-53" value="domain_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-51" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-25" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="GH2g80-cBXHe62XdPV4N-18" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-26" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-25" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-27" value="app_name" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-25" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-28" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="GH2g80-cBXHe62XdPV4N-18" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-29" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-28" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="GH2g80-cBXHe62XdPV4N-30" value="update_date" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="GH2g80-cBXHe62XdPV4N-28" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-1" value="Flow_History" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="480" width="180" height="240" as="geometry">
|
||||
<mxRectangle x="320" y="670" width="110" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-23" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="AJLbZMHdl7kzV18ppMoM-1" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-24" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-23" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-25" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-23" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-2" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="AJLbZMHdl7kzV18ppMoM-1" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-3" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-2" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-4" value="flow_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-2" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-5" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-1" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-6" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-5" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-7" value="domain_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-5" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-8" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-1" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-9" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-8" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-10" value="spaceid" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-8" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-11" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-1" vertex="1">
|
||||
<mxGeometry y="150" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-12" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-11" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-13" value="appid" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-11" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-17" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-1" vertex="1">
|
||||
<mxGeometry y="180" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-18" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="AJLbZMHdl7kzV18ppMoM-17" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-19" value="event_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="AJLbZMHdl7kzV18ppMoM-17" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-14" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-1" vertex="1">
|
||||
<mxGeometry y="210" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-15" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" parent="AJLbZMHdl7kzV18ppMoM-14" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-16" value="content" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" parent="AJLbZMHdl7kzV18ppMoM-14" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-40" value="Action" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
|
||||
<mxGeometry x="130" y="720" width="180" height="150" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-41" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="AJLbZMHdl7kzV18ppMoM-40" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-42" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-41" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-43" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-41" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-44" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-40" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-45" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-44" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-46" value="アクション名" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-44" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-47" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-40" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-48" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-47" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-49" value="説明" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-47" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-50" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="AJLbZMHdl7kzV18ppMoM-40" vertex="1">
|
||||
<mxGeometry y="120" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-51" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-50" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="AJLbZMHdl7kzV18ppMoM-52" value="属性定義(json)" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="AJLbZMHdl7kzV18ppMoM-50" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-7" value="UserDomain" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="100" width="180" height="120" as="geometry">
|
||||
<mxRectangle x="590" y="60" width="100" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-8" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="NCbWPNvujZOKFJAbQVH6-7" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-9" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-8" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-10" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-8" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-11" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="NCbWPNvujZOKFJAbQVH6-7" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-12" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-11" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-13" value="user_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-11" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-44" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="NCbWPNvujZOKFJAbQVH6-7" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-45" value="FK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=0;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-44" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-46" value="domain_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=0;overflow=hidden;whiteSpace=wrap;html=1;" parent="NCbWPNvujZOKFJAbQVH6-44" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-42" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERzeroToMany;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=1;" parent="1" source="QUGX19b9cPh8sdE7zjoR-2" target="inIyfaXWTM6ArMeTnGn6-2" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="160" y="350" as="sourcePoint"/>
|
||||
<mxPoint x="260" y="250" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-47" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERmandOne;startArrow=ERmandOne;rounded=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="NCbWPNvujZOKFJAbQVH6-11" target="inIyfaXWTM6ArMeTnGn6-2" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="230" y="390" as="sourcePoint"/>
|
||||
<mxPoint x="330" y="290" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-49" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERmandOne;startArrow=ERmandOne;rounded=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="inIyfaXWTM6ArMeTnGn6-15" target="NCbWPNvujZOKFJAbQVH6-44" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="200" y="400" as="sourcePoint"/>
|
||||
<mxPoint x="300" y="300" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-50" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERzeroToMany;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=1;" parent="1" source="inIyfaXWTM6ArMeTnGn6-15" target="NCbWPNvujZOKFJAbQVH6-51" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="140" y="430" as="sourcePoint"/>
|
||||
<mxPoint x="740" y="445" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-54" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERzeroToMany;endFill=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;curved=1;" parent="1" source="NCbWPNvujZOKFJAbQVH6-51" target="8Zu1yShcSHxMs68hy39H-5" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="210" y="450" as="sourcePoint"/>
|
||||
<mxPoint x="310" y="350" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="NCbWPNvujZOKFJAbQVH6-55" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERzeroToMany;endFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;curved=1;" parent="1" source="8Zu1yShcSHxMs68hy39H-2" target="AJLbZMHdl7kzV18ppMoM-2" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="270" y="470" as="sourcePoint"/>
|
||||
<mxPoint x="370" y="370" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-1" value="EventAction" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
|
||||
<mxGeometry x="-110" y="570" width="180" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-2" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" parent="dFNVox72oq8u18PzFUgJ-1" vertex="1">
|
||||
<mxGeometry y="30" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-3" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="dFNVox72oq8u18PzFUgJ-2" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-4" value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" parent="dFNVox72oq8u18PzFUgJ-2" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-5" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="dFNVox72oq8u18PzFUgJ-1" vertex="1">
|
||||
<mxGeometry y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-6" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="dFNVox72oq8u18PzFUgJ-5" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-7" value="event_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="dFNVox72oq8u18PzFUgJ-5" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-8" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" parent="dFNVox72oq8u18PzFUgJ-1" vertex="1">
|
||||
<mxGeometry y="90" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-9" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" parent="dFNVox72oq8u18PzFUgJ-8" vertex="1">
|
||||
<mxGeometry width="30" height="30" as="geometry">
|
||||
<mxRectangle width="30" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-10" value="action_id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" parent="dFNVox72oq8u18PzFUgJ-8" vertex="1">
|
||||
<mxGeometry x="30" width="150" height="30" as="geometry">
|
||||
<mxRectangle width="150" height="30" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-16" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERmandOne;startArrow=ERmandOne;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;curved=1;" parent="1" source="dFNVox72oq8u18PzFUgJ-5" target="GH2g80-cBXHe62XdPV4N-2" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="290" y="800" as="sourcePoint"/>
|
||||
<mxPoint x="390" y="700" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dFNVox72oq8u18PzFUgJ-17" value="" style="edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERmandOne;startArrow=ERmandOne;rounded=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="AJLbZMHdl7kzV18ppMoM-41" target="dFNVox72oq8u18PzFUgJ-8" edge="1">
|
||||
<mxGeometry width="100" height="100" relative="1" as="geometry">
|
||||
<mxPoint x="-100" y="900" as="sourcePoint"/>
|
||||
<mxPoint y="800" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
<diagram id="yu2qkxLoxjZt0KdZdE3U" name="ページ3">
|
||||
<mxGraphModel dx="1434" dy="884" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
document/Kintone自動作成ツールのプラグインについて.xlsx
Normal file
BIN
document/Kintone自動作成ツールのプラグインについて.xlsx
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
document/action-property.png
Normal file
BIN
document/action-property.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
document/kintone開発自動化ツール UIデザイン案.pptx
Normal file
BIN
document/kintone開発自動化ツール UIデザイン案.pptx
Normal file
Binary file not shown.
147
document/サイトマップ.drawio
Normal file
147
document/サイトマップ.drawio
Normal file
@@ -0,0 +1,147 @@
|
||||
<mxfile host="app.diagrams.net" modified="2024-02-21T05:42:02.026Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" etag="T2S5cjvthSOlO5DmGw-C" version="23.1.5" type="device">
|
||||
<diagram id="Z6uZM46JtkVaKDzPjE9h" name="サイトマップ">
|
||||
<mxGraphModel dx="1434" dy="820" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-1" target="Gi77RX5G2m4J9-6cMje4-13" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-1" value="テナント登録" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.login;" parent="1" vertex="1">
|
||||
<mxGeometry x="60" y="50" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-2" value="Admin Login" style="html=1;whiteSpace=wrap;strokeColor=#2D7600;fillColor=#60a917;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#ffffff;sketch=0;shape=mxgraph.sitemap.login;" parent="1" vertex="1">
|
||||
<mxGeometry x="60" y="270" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-5" target="Gi77RX5G2m4J9-6cMje4-7" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-5" target="Gi77RX5G2m4J9-6cMje4-7" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-12" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-5" target="Gi77RX5G2m4J9-6cMje4-11" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-5" value="Home" style="html=1;whiteSpace=wrap;strokeColor=#2D7600;fillColor=#60a917;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#ffffff;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="240" y="270" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-2" target="Gi77RX5G2m4J9-6cMje4-5" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-42" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-7" target="Gi77RX5G2m4J9-6cMje4-41" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-7" value="ユーザー登録" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="220" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-11" value="ドメイン登録" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="340" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-13" value="テナント管理者作成" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.login;" parent="1" vertex="1">
|
||||
<mxGeometry x="240" y="50" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-15" value="ライセンス情報" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="10" width="90" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-16" value="Adminユーザー" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" parent="1" vertex="1">
|
||||
<mxGeometry x="480" y="90" width="90" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-13" target="Gi77RX5G2m4J9-6cMje4-15" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-13" target="Gi77RX5G2m4J9-6cMje4-16" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-19" value="テナントDB<br>作成" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" parent="1" vertex="1">
|
||||
<mxGeometry x="550" y="50" width="90" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-20" target="Gi77RX5G2m4J9-6cMje4-21" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-20" value="Login" style="html=1;whiteSpace=wrap;strokeColor=#005700;fillColor=#008a00;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;sketch=0;shape=mxgraph.sitemap.login;fontColor=#ffffff;" parent="1" vertex="1">
|
||||
<mxGeometry x="50" y="610" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-24" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-21" target="Gi77RX5G2m4J9-6cMje4-25" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="430" y="645" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-21" value="Home" style="html=1;whiteSpace=wrap;strokeColor=#005700;fillColor=#008a00;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#ffffff;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="230" y="610" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-27" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-25" target="Gi77RX5G2m4J9-6cMje4-26" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-25" value="アプリ一覧" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.news;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="610" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-26" target="Gi77RX5G2m4J9-6cMje4-28" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-26" value="フロー一覧" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.news;" parent="1" vertex="1">
|
||||
<mxGeometry x="620" y="610" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-40" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-28" target="Gi77RX5G2m4J9-6cMje4-39" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-28" value="フローエディタ" style="html=1;whiteSpace=wrap;strokeColor=#005700;fillColor=#008a00;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#ffffff;sketch=0;shape=mxgraph.sitemap.news;" parent="1" vertex="1">
|
||||
<mxGeometry x="800" y="610" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-30" target="Gi77RX5G2m4J9-6cMje4-32" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-30" value="設計書取込" style="html=1;whiteSpace=wrap;strokeColor=#005700;fillColor=#008a00;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#ffffff;sketch=0;shape=mxgraph.sitemap.news;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="715" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-21" target="Gi77RX5G2m4J9-6cMje4-30" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-32" value="取込結果表示" style="html=1;whiteSpace=wrap;strokeColor=#005700;fillColor=#008a00;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#ffffff;sketch=0;shape=mxgraph.sitemap.news;" parent="1" vertex="1">
|
||||
<mxGeometry x="620" y="715" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-37" value="設計書ダウロード" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.news;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="825" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-21" target="Gi77RX5G2m4J9-6cMje4-37" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-39" value="フロー履歴管理" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.news;" parent="1" vertex="1">
|
||||
<mxGeometry x="980" y="610" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-41" value="ALC設定" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="620" y="220" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-43" value="管理ドメイン設定" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="800" y="220" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-41" target="Gi77RX5G2m4J9-6cMje4-43" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-45" value="プロファイル" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="935" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-46" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-21" target="Gi77RX5G2m4J9-6cMje4-45" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-50" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="Gi77RX5G2m4J9-6cMje4-47" target="Gi77RX5G2m4J9-6cMje4-49" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-47" value="プロファイル" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="440" y="450" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-48" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-5" target="Gi77RX5G2m4J9-6cMje4-47" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-49" value="ライセンス情報" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="620" y="450" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-51" value="ライセンス情報" style="html=1;whiteSpace=wrap;strokeColor=none;fillColor=#0079D6;labelPosition=center;verticalLabelPosition=middle;verticalAlign=top;align=center;fontSize=12;outlineConnect=0;spacingTop=-6;fontColor=#FFFFFF;sketch=0;shape=mxgraph.sitemap.home;" parent="1" vertex="1">
|
||||
<mxGeometry x="620" y="935" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Gi77RX5G2m4J9-6cMje4-52" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="Gi77RX5G2m4J9-6cMje4-45" target="Gi77RX5G2m4J9-6cMje4-51" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
document/収支明細管理設計書.xlsx
Normal file
BIN
document/収支明細管理設計書.xlsx
Normal file
Binary file not shown.
BIN
document/日報設計書new.xlsx
Normal file
BIN
document/日報設計書new.xlsx
Normal file
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
KAB_BACKEND_URL="http://127.0.0.1:8000/api/v1/"
|
||||
|
||||
KAB_BACKEND_URL="https://kab-backend.azurewebsites.net/"
|
||||
#KAB_BACKEND_URL="http://127.0.0.1:8000/"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
VUE_BACKEND_URL="http://localhost:8000/api/"
|
||||
|
||||
#KAB_BACKEND_URL="https://kab-backend.azurewebsites.net/"
|
||||
KAB_BACKEND_URL="http://127.0.0.1:8000/"
|
||||
|
||||
3
frontend/.gitignore
vendored
3
frontend/.gitignore
vendored
@@ -35,3 +35,6 @@ yarn-error.log*
|
||||
|
||||
# local .env files
|
||||
.env.local*
|
||||
|
||||
# pnpm
|
||||
pnpm-lock.yaml
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="ja-jp">
|
||||
<head>
|
||||
<title><%= productName %></title>
|
||||
|
||||
|
||||
72
frontend/package-lock.json
generated
72
frontend/package-lock.json
generated
@@ -10,13 +10,16 @@
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.16.4",
|
||||
"axios": "^1.4.0",
|
||||
"pinia": "^2.1.6",
|
||||
"quasar": "^2.6.0",
|
||||
"uuid": "^9.0.0",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app-vite": "^1.3.0",
|
||||
"@types/node": "^12.20.21",
|
||||
"@types/uuid": "^9.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
@@ -28,8 +31,9 @@
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || ^16 || ^14.19",
|
||||
"node": "^20 ||^18 || ^16 || ^14.19",
|
||||
"npm": ">= 6.13.4",
|
||||
"pnpm": ">=8.6.0",
|
||||
"yarn": ">= 1.21.1"
|
||||
}
|
||||
},
|
||||
@@ -544,6 +548,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.3.tgz",
|
||||
"integrity": "sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz",
|
||||
@@ -4070,6 +4080,56 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.6.tgz",
|
||||
"integrity": "sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"vue-demi": ">=0.14.5"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.4.0",
|
||||
"typescript": ">=4.4.4",
|
||||
"vue": "^2.6.14 || ^3.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
},
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/vue-demi": {
|
||||
"version": "0.14.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz",
|
||||
"integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.25",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz",
|
||||
@@ -4946,7 +5006,7 @@
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -5045,6 +5105,14 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "kintone-app-builder",
|
||||
"version": "0.0.1",
|
||||
"name": "kintone-automate",
|
||||
"version": "0.2.0",
|
||||
"description": "Kintoneアプリの自動生成とデプロイを支援ツールです",
|
||||
"productName": "Kintone App Builder",
|
||||
"productName": "kintone Automate",
|
||||
"author": "maxiaozhe@alicorns.co.jp <maxiaozhe@alicorns.co.jp>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -10,18 +10,24 @@
|
||||
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||
"test": "echo \"No test specified\" && exit 0",
|
||||
"dev": "quasar dev",
|
||||
"build": "quasar build"
|
||||
"dev:local": "set \"LOCAL=true\" && quasar dev",
|
||||
"build": "set \"SOURCE_MAP=false\" && quasar build",
|
||||
"build:dev":"set \"SOURCE_MAP=true\" && quasar build"
|
||||
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.16.4",
|
||||
"axios": "^1.4.0",
|
||||
"pinia": "^2.1.6",
|
||||
"quasar": "^2.6.0",
|
||||
"uuid": "^9.0.0",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app-vite": "^1.3.0",
|
||||
"@types/node": "^12.20.21",
|
||||
"@types/uuid": "^9.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
@@ -33,8 +39,9 @@
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || ^16 || ^14.19",
|
||||
"node": "^20 ||^18 || ^16 || ^14.19",
|
||||
"npm": ">= 6.13.4",
|
||||
"yarn": ">= 1.21.1"
|
||||
"yarn": ">= 1.21.1",
|
||||
"pnpm": ">=8.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
8
frontend/public/web.config
Normal file
8
frontend/public/web.config
Normal file
@@ -0,0 +1,8 @@
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
|
||||
<mimeMap fileExtension=".woff2" mimeType="application/font-woff2" />
|
||||
</staticContent>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -10,10 +10,14 @@
|
||||
|
||||
|
||||
const { configure } = require('quasar/wrappers');
|
||||
const dotenv = require('dotenv').config().parsed;
|
||||
const package = require('./package.json');
|
||||
const envPath = process.env.LOCAL==='true'?'.env.development':'.env';
|
||||
const dotenv = require('dotenv').config({path:envPath}).parsed;
|
||||
console.log('dotenv=>',dotenv);
|
||||
// const package = require('./package.json');
|
||||
const { Notify } = require('quasar');
|
||||
const version = package.version;
|
||||
const version = process.env.npm_package_version;
|
||||
const productName=process.env.npm_package_productName;
|
||||
// console.log(process.env);
|
||||
module.exports = configure(function (/* ctx */) {
|
||||
return {
|
||||
eslint: {
|
||||
@@ -32,7 +36,8 @@ module.exports = configure(function (/* ctx */) {
|
||||
// --> boot files are part of "main.js"
|
||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||
boot: [
|
||||
'axios'
|
||||
'axios',
|
||||
'error-handler'
|
||||
],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||
@@ -49,7 +54,6 @@ module.exports = configure(function (/* ctx */) {
|
||||
// 'themify',
|
||||
// 'line-awesome',
|
||||
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
|
||||
|
||||
'roboto-font', // optional, you are not bound to it
|
||||
'material-icons', // optional, you are not bound to it
|
||||
],
|
||||
@@ -60,6 +64,7 @@ module.exports = configure(function (/* ctx */) {
|
||||
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
|
||||
node: 'node16'
|
||||
},
|
||||
sourcemap:process.env.SOURCE_MAP === 'true',
|
||||
|
||||
vueRouterMode: 'hash', // available values: 'hash', 'history'
|
||||
// vueRouterBase,
|
||||
@@ -70,7 +75,7 @@ module.exports = configure(function (/* ctx */) {
|
||||
|
||||
// publicPath: '/',
|
||||
// analyze: true,
|
||||
env: { ...dotenv, version },
|
||||
env: { ...dotenv, version ,productName},
|
||||
// rawDefine: {}
|
||||
// ignorePublicFolder: true,
|
||||
// minify: false,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { boot } from 'quasar/wrappers';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import {router} from 'src/router';
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
@@ -15,7 +16,26 @@ declare module '@vue/runtime-core' {
|
||||
// "export default () => {}" function below (which runs individually
|
||||
// for each client)
|
||||
const api:AxiosInstance = axios.create({ baseURL: process.env.KAB_BACKEND_URL });
|
||||
|
||||
const token=localStorage.getItem('token')||'';
|
||||
if(token!==''){
|
||||
api.defaults.headers["Authorization"]='Bearer ' + token;
|
||||
}
|
||||
//axios例外キャプチャー
|
||||
api.interceptors.response.use(
|
||||
(response)=>response,
|
||||
(error)=>{
|
||||
if (error.response && error.response.status === 401) {
|
||||
// 認証エラーの場合再ログインする
|
||||
console.error('(; ゚Д゚)/認証エラー(401):', error);
|
||||
localStorage.removeItem('token');
|
||||
router.replace({
|
||||
path:"/login",
|
||||
query:{redirect:router.currentRoute.value.fullPath}
|
||||
});
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
export default boot(({ app }) => {
|
||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||
|
||||
|
||||
20
frontend/src/boot/error-handler.ts
Normal file
20
frontend/src/boot/error-handler.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// src/boot/error-handler.ts
|
||||
import { boot } from 'quasar/wrappers';
|
||||
import { Router } from 'vue-router';
|
||||
import { App } from 'vue';
|
||||
|
||||
export default boot(({ app, router }: { app: App<Element>; router: Router }) => {
|
||||
app.config.errorHandler = (err: any, instance: any, info: string) => {
|
||||
if (err.response && err.response.status === 401) {
|
||||
// 認証エラーの場合再ログインする
|
||||
console.error('(; ゚Д゚)/認証エラー(401):', err, info);
|
||||
localStorage.removeItem('token');
|
||||
router.replace({
|
||||
path:"/login",
|
||||
query:{redirect:router.currentRoute.value.fullPath}
|
||||
});
|
||||
} else {
|
||||
console.error('(; ゚Д゚)例外:', err, info);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,6 +1,17 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table :title="name+'一覧'" row-key="name" :selection="type" v-model:selected="selected" :columns="columns" :rows="rows" />
|
||||
<div v-if="!isLoaded" class="spinner flex flex-center">
|
||||
<q-spinner color="primary" size="3em" />
|
||||
</div>
|
||||
<q-table v-else row-key="index" :selection="type" v-model:selected="selected" :columns="columns" :rows="rows"
|
||||
class="action-table"
|
||||
flat bordered
|
||||
virtual-scroll
|
||||
:pagination="pagination"
|
||||
:rows-per-page-options="[0]"
|
||||
:filter="filter"
|
||||
>
|
||||
</q-table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@@ -11,30 +22,41 @@ export default {
|
||||
name: 'actionSelect',
|
||||
props: {
|
||||
name: String,
|
||||
type: String
|
||||
type: String,
|
||||
filter:String
|
||||
},
|
||||
setup() {
|
||||
const columns = [
|
||||
{ name: 'name', required: true,label: 'アクション名',align: 'left',field: 'name',sortable: true},
|
||||
{ name: 'desc', align: 'left', label: '説明', field: 'desc', sortable: true },
|
||||
{ name: 'content', label: '内容', field: 'content', sortable: true }
|
||||
]
|
||||
const rows = reactive([])
|
||||
setup(props) {
|
||||
const isLoaded=ref(false);
|
||||
const columns = [
|
||||
{ name: 'name', required: true,label: 'アクション名',align: 'left',field: 'name',sortable: true},
|
||||
{ name: 'desc', align: 'left', label: '説明', field: 'desc', sortable: true },
|
||||
// { name: 'content', label: '内容', field: 'content', sortable: true }
|
||||
];
|
||||
const rows = reactive([])
|
||||
onMounted(async () => {
|
||||
await api.get('http://127.0.0.1:8000/api/kintone/2').then(res =>{
|
||||
res.data.forEach((item) =>
|
||||
const res =await api.get('api/actions');
|
||||
res.data.forEach((item,index) =>
|
||||
{
|
||||
rows.push({name:item.name,desc:item.desc,content:item.content});
|
||||
}
|
||||
)
|
||||
});
|
||||
rows.push({index,name:item.name,desc:item.title,outputPoints:item.outputpoints,property:item.property});
|
||||
});
|
||||
isLoaded.value=true;
|
||||
});
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
selected: ref([]),
|
||||
pagination:ref({
|
||||
rowsPerPage:0
|
||||
}),
|
||||
isLoaded,
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.action-table{
|
||||
min-height: 10vh;
|
||||
max-height: 68vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
import { AppInfo, AppSeed } from './models';
|
||||
import { ref, defineComponent, watch, onMounted , toRefs } from 'vue';
|
||||
import { api } from 'boot/axios';
|
||||
import { promises } from 'dns';
|
||||
import { useAuthStore } from 'src/stores/useAuthStore';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -44,12 +44,13 @@ export default defineComponent({
|
||||
},
|
||||
setup(props) {
|
||||
const { app } = toRefs(props);
|
||||
const authStore = useAuthStore();
|
||||
const appinfo = ref<AppInfo>({
|
||||
appId: "",
|
||||
name: "",
|
||||
description: ""
|
||||
});
|
||||
const link= ref('https://mfu07rkgnb7c.cybozu.com/k/' + app.value);
|
||||
const link= ref(`${authStore.currentDomain.kintoneUrl}/k/${app.value}`);
|
||||
const getAppInfo = async (appId:string|undefined) => {
|
||||
if(!appId){
|
||||
return;
|
||||
@@ -59,7 +60,7 @@ export default defineComponent({
|
||||
let retry =0;
|
||||
while(retry<=3 && result && result.appId!==appId){
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
const response = await api.get('app', {
|
||||
const response = await api.get('api/v1/app', {
|
||||
params:{
|
||||
app: appId
|
||||
}
|
||||
@@ -73,7 +74,7 @@ export default defineComponent({
|
||||
|
||||
watch(app, async (newApp) => {
|
||||
appinfo.value = await getAppInfo(newApp);
|
||||
link.value = 'https://mfu07rkgnb7c.cybozu.com/k/' + newApp;
|
||||
link.value = `${authStore.currentDomain.kintoneUrl}/k/${newApp}`;
|
||||
}, { immediate: true });
|
||||
|
||||
const linkClick=(ev : MouseEvent)=>{
|
||||
@@ -82,7 +83,7 @@ export default defineComponent({
|
||||
};
|
||||
onMounted(async ()=>{
|
||||
appinfo.value = await getAppInfo(app.value);
|
||||
link.value = 'https://mfu07rkgnb7c.cybozu.com/k/' + app.value;
|
||||
link.value = `${authStore.currentDomain.kintoneUrl}/k/${app.value}`;
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,41 +1,100 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table :title="name+'一覧'" :selection="type" v-model:selected="selected" :columns="columns" :rows="rows" />
|
||||
<div class="q-px-xs">
|
||||
<div v-if="!isLoaded" class="spinner flex flex-center">
|
||||
<q-spinner color="primary" size="3em" />
|
||||
</div>
|
||||
<q-table v-else class="app-table" :selection="type" row-key="id" v-model:selected="selected" flat bordered
|
||||
virtual-scroll :columns="columns" :rows="rows" :pagination="pagination" :rows-per-page-options="[0]"
|
||||
:filter="filter" style="max-height: 65vh;">
|
||||
<template v-slot:body-cell-description="props">
|
||||
<q-td :props="props">
|
||||
<q-scroll-area class="description-cell">
|
||||
<div v-html="props.row.description"></div>
|
||||
</q-scroll-area>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref,onMounted,reactive } from 'vue'
|
||||
<script lang="ts">
|
||||
import { ref, onMounted, reactive, watchEffect } from 'vue'
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
export default {
|
||||
name: 'appSelect',
|
||||
name: 'AppSelect',
|
||||
props: {
|
||||
name: String,
|
||||
type: String
|
||||
type: String,
|
||||
filter: String,
|
||||
updateExternalSelectAppInfo: {
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const columns = [
|
||||
{ name: 'id', required: true,label: 'アプリID',align: 'left',field: 'id',sortable: true},
|
||||
{ name: 'name', align: 'center', label: 'アプリ名', field: 'name', sortable: true },
|
||||
{ name: 'creator', label: '作成者', field: 'creator', sortable: true },
|
||||
{ name: 'createdate', label: '作成日時', field: 'createdate' }
|
||||
]
|
||||
const rows = reactive([])
|
||||
onMounted( () => {
|
||||
api.get('allapps').then(res =>{
|
||||
res.data.apps.forEach((item) =>
|
||||
{
|
||||
rows.push({id:item.appId,name:item.name,creator:item.creator.name,createdate:item.createdAt});
|
||||
}
|
||||
)
|
||||
});
|
||||
setup(props) {
|
||||
const columns = [
|
||||
{ name: 'id', required: true, label: 'ID', align: 'left', field: 'id', sortable: true },
|
||||
{ name: 'name', label: 'アプリ名', field: 'name', sortable: true, align: 'left' },
|
||||
{ name: 'description', label: '概要', field: 'description', align: 'left', sortable: false },
|
||||
{ name: 'createdate', label: '作成日時', field: 'createdate', align: 'left' }
|
||||
]
|
||||
const isLoaded = ref(false);
|
||||
const rows: any[] = reactive([]);
|
||||
const selected = ref([])
|
||||
|
||||
watchEffect(()=>{
|
||||
if (selected.value && selected.value[0] && props.updateExternalSelectAppInfo) {
|
||||
props.updateExternalSelectAppInfo(selected.value[0])
|
||||
}
|
||||
});
|
||||
onMounted(() => {
|
||||
api.get('api/v1/allapps').then(res => {
|
||||
res.data.apps.forEach((item: any) => {
|
||||
rows.push({
|
||||
id: item.appId,
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
createdate: dateFormat(item.createdAt)
|
||||
});
|
||||
});
|
||||
isLoaded.value = true;
|
||||
});
|
||||
});
|
||||
|
||||
const dateFormat = (dateStr: string) => {
|
||||
const date = new Date(dateStr);
|
||||
const pad = (num: number) => num.toString().padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
const month = pad(date.getMonth() + 1);
|
||||
const day = pad(date.getDate());
|
||||
const hours = pad(date.getHours());
|
||||
const minutes = pad(date.getMinutes());
|
||||
const seconds = pad(date.getSeconds());
|
||||
return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
selected: ref([]),
|
||||
selected,
|
||||
isLoaded,
|
||||
pagination: ref({
|
||||
rowsPerPage: 10
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.description-cell {
|
||||
height: 60px;
|
||||
width: 300px;
|
||||
max-height: 60px;
|
||||
max-width: 300px;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
min-height: 300px;
|
||||
min-width: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
107
frontend/src/components/ConditionEditor/ConditionEditor.vue
Normal file
107
frontend/src/components/ConditionEditor/ConditionEditor.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<show-dialog v-model:visible="showflg" name="条件エディタ" @close="closeDg" min-width="60vw" min-height="60vh">
|
||||
<template v-slot:toolbar>
|
||||
<q-btn flat round dense icon="more_vert" >
|
||||
<q-menu auto-close anchor="bottom start">
|
||||
<q-list>
|
||||
<q-item clickable @click="copyCondition()">
|
||||
<q-item-section avatar><q-icon name="content_copy" ></q-icon></q-item-section>
|
||||
<q-item-section >コピー</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="pasteCondition()">
|
||||
<q-item-section avatar><q-icon name="content_paste" ></q-icon></q-item-section>
|
||||
<q-item-section >貼り付け</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</template>
|
||||
<NodeCondition v-model:conditionTree="tree"></NodeCondition>
|
||||
</show-dialog>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref ,watchEffect} from 'vue';
|
||||
import ShowDialog from '../../components/ShowDialog.vue';
|
||||
import NodeCondition from './NodeCondition.vue';
|
||||
import { ConditionTree } from '../../types/Conditions';
|
||||
import { useQuasar } from 'quasar';
|
||||
export default defineComponent({
|
||||
name: 'ConditionObject',
|
||||
components: {
|
||||
ShowDialog,
|
||||
NodeCondition,
|
||||
},
|
||||
props: {
|
||||
conditionTree: {
|
||||
type: ConditionTree,
|
||||
default: null
|
||||
},
|
||||
show:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
emits:[
|
||||
"closed",
|
||||
"update:conditionTree",
|
||||
"update:show"
|
||||
],
|
||||
setup(props,context) {
|
||||
const appDg = ref();
|
||||
const $q=useQuasar();
|
||||
const tree = ref(props.conditionTree);
|
||||
const closeDg = (val:string) => {
|
||||
if (val == 'OK') {
|
||||
if(tree.value.root.children.length===0){
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: `条件式を設定してください。`
|
||||
});
|
||||
}
|
||||
context.emit("update:conditionTree",tree.value);
|
||||
}
|
||||
showflg.value=false;
|
||||
context.emit("update:show",false);
|
||||
context.emit("closed",val);
|
||||
};
|
||||
const showflg =ref(props.show);
|
||||
//条件式をコピーする
|
||||
const copyCondition=()=>{
|
||||
if (navigator.clipboard) {
|
||||
const jsonData=tree.value.toJson();
|
||||
navigator.clipboard.writeText(jsonData).then(() => {
|
||||
console.log('Text successfully copied to clipboard');
|
||||
},
|
||||
(err) => {
|
||||
console.error('Error in copying text: ', err);
|
||||
});
|
||||
} else {
|
||||
console.log('Clipboard API not available');
|
||||
}
|
||||
};
|
||||
//条件式を貼り付ける
|
||||
const pasteCondition=async ()=>{
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
console.log('Text from clipboard:', text);
|
||||
tree.value.fromJson(text);
|
||||
} catch (err) {
|
||||
console.error('Failed to read text from clipboard: ', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
watchEffect(() => {
|
||||
showflg.value=props.show;
|
||||
});
|
||||
|
||||
return {
|
||||
tree,
|
||||
appDg,
|
||||
closeDg,
|
||||
showflg,
|
||||
copyCondition,
|
||||
pasteCondition
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
96
frontend/src/components/ConditionEditor/ConditionObject.vue
Normal file
96
frontend/src/components/ConditionEditor/ConditionObject.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<q-field v-model="selectedObject" labelColor="primary" class="condition-object"
|
||||
:clearable="isSelected" stack-label :dense="true" :outlined="true" >
|
||||
<template v-slot:control >
|
||||
<q-chip color="primary" text-color="white" v-if="isSelected && selectedObject.objectType==='field'" :dense="true" class="selected-obj">
|
||||
{{ selectedObject.name }}
|
||||
</q-chip>
|
||||
<q-chip color="info" text-color="white" v-if="isSelected && selectedObject.objectType==='variable'" :dense="true" class="selected-obj">
|
||||
{{ selectedObject.name }}
|
||||
</q-chip>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" class="cursor-pointer" @click="showDg"/>
|
||||
</template>
|
||||
</q-field>
|
||||
<show-dialog v-model:visible="show" name="条件設定項目一覧" @close="closeDg" width="600px">
|
||||
<template v-slot:toolbar>
|
||||
<q-input dense debounce="200" v-model="filter" placeholder="検索" clearable>
|
||||
<template v-slot:before>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<condition-objects ref="appDg" name="フィールド" type="single" :filter="filter" :appId="store.appInfo?.appId" :vars="vars"></condition-objects>
|
||||
</show-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, ref ,watchEffect,computed} from 'vue';
|
||||
import ShowDialog from '../ShowDialog.vue';
|
||||
import ConditionObjects from '../ConditionObjects.vue';
|
||||
import { useFlowEditorStore } from '../../stores/flowEditor';
|
||||
import {IActionFlow,IActionNode,IActionVariable} from '../../types/ActionTypes';
|
||||
export default defineComponent({
|
||||
name: 'ConditionObject',
|
||||
components: {
|
||||
ShowDialog,
|
||||
ConditionObjects
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const appDg = ref();
|
||||
const show = ref(false);
|
||||
const selectedObject = ref(props.modelValue);
|
||||
const store = useFlowEditorStore();
|
||||
const isSelected = computed(()=>{
|
||||
return selectedObject.value!==null && typeof selectedObject.value === 'object' && ('name' in selectedObject.value)
|
||||
});
|
||||
let vars:IActionVariable[] =[];
|
||||
if(store.currentFlow!==undefined && store.activeNode!==undefined ){
|
||||
vars =store.currentFlow.getVarNames(store.activeNode);
|
||||
}
|
||||
const filter=ref('');
|
||||
const showDg = () => {
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
const closeDg = (val:string) => {
|
||||
if (val == 'OK') {
|
||||
selectedObject.value = appDg.value.selected[0];
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', selectedObject.value);
|
||||
});
|
||||
|
||||
return {
|
||||
store,
|
||||
appDg,
|
||||
show,
|
||||
showDg,
|
||||
closeDg,
|
||||
selectedObject,
|
||||
vars:reactive(vars),
|
||||
isSelected,
|
||||
filter
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.condition-object{
|
||||
min-width: 200px;
|
||||
max-height: 40px;
|
||||
padding: 2px;
|
||||
}
|
||||
.selected-obj{
|
||||
margin: 0px;
|
||||
}
|
||||
</style>
|
||||
269
frontend/src/components/ConditionEditor/NodeCondition.vue
Normal file
269
frontend/src/components/ConditionEditor/NodeCondition.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<!-- <q-toolbar class="bg-grey-3" flat dense round icon="menu" aria-label="Menu" @click.stop>
|
||||
<q-toolbar-title>条件エディタ</q-toolbar-title>
|
||||
<q-space></q-space>
|
||||
<q-btn flat round dense icon="info" color="blue" @click="showingCondition=!showingCondition"></q-btn>
|
||||
</q-toolbar> -->
|
||||
<div class="q-pa-md">
|
||||
<q-tree :nodes="[tree.root]" node-key="index" children-key="children"
|
||||
tick-strategy="strict" v-model:ticked="ticked" :expanded="expanded" default-expand-all dense color="primary" >
|
||||
<template v-slot:header-root="prop">
|
||||
<!-- root -->
|
||||
<div class="row items-center" @click.stop>
|
||||
<q-select v-model="prop.node.logicalOperator" :options="logicalOperators" filled outlined dense></q-select>
|
||||
<q-btn flat round dense icon="more_horiz" size="sm" >
|
||||
<q-menu auto-close anchor="top right">
|
||||
<q-list>
|
||||
<q-item clickable @click="addGroup(prop.node, LogicalOperator.AND)">
|
||||
<q-item-section avatar><q-icon name="playlist_add" ></q-icon></q-item-section>
|
||||
<q-item-section>グループの追加</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="addCondition(prop.node)">
|
||||
<q-item-section avatar><q-icon name="add_circle_outline" ></q-icon></q-item-section>
|
||||
<q-item-section >条件式の追加</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:header-generic="prop">
|
||||
<!-- logic group -->
|
||||
<div v-if="prop.node.type !== NodeType.Condition" class="row items-center" @click.stop>
|
||||
<q-select v-model="prop.node.logicalOperator" :options="logicalOperators" :outlined="true" :filled="true" :dense="true"></q-select>
|
||||
<q-btn flat round dense icon="more_horiz" size="sm" >
|
||||
<q-menu auto-close anchor="top right">
|
||||
<q-list>
|
||||
<q-item clickable @click="moveUp(prop.node)">
|
||||
<q-item-section avatar><q-icon name="arrow_upward" ></q-icon></q-item-section>
|
||||
<q-item-section >一つ上に移動</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="moveDown(prop.node)">
|
||||
<q-item-section avatar><q-icon name="arrow_downward" ></q-icon></q-item-section>
|
||||
<q-item-section >一つ下に移動</q-item-section>
|
||||
</q-item>
|
||||
<q-separator inset/>
|
||||
<q-item clickable @click="addGroup(prop.node, LogicalOperator.AND)">
|
||||
<q-item-section avatar><q-icon name="playlist_add" ></q-icon></q-item-section>
|
||||
<q-item-section >グループ追加</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="addCondition(prop.node)">
|
||||
<q-item-section avatar><q-icon name="add_circle_outline" ></q-icon></q-item-section>
|
||||
<q-item-section >条件式追加</q-item-section>
|
||||
</q-item>
|
||||
<q-separator inset/>
|
||||
<q-item clickable @click="splitGroup(prop.node)">
|
||||
<q-item-section avatar><q-icon name="playlist_remove" color="negative"></q-icon></q-item-section>
|
||||
<q-item-section >グループ化解除</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="removeNode(prop.node)">
|
||||
<q-item-section avatar><q-icon name="delete" color="negative"></q-icon></q-item-section>
|
||||
<q-item-section >削除</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
<!-- condition -->
|
||||
<div @click.stop @keypress.stop v-else >
|
||||
<div class="row no-wrap items-center">
|
||||
<ConditionObject v-bind="prop.node" v-model="prop.node.object" class="col-4"></ConditionObject>
|
||||
<q-select v-model="prop.node.operator" :options="operators" class="operator" :outlined="true" :dense="true"></q-select>
|
||||
<q-input v-if="!prop.node.object || !('options' in prop.node.object)"
|
||||
v-model="prop.node.value"
|
||||
class="condition-value" :outlined="true" :dense="true" ></q-input>
|
||||
<q-select v-if="prop.node.object && ('options' in prop.node.object)"
|
||||
v-model="prop.node.value"
|
||||
:options="objectValueOptions(prop.node.object.options)"
|
||||
clearable
|
||||
value-key="index"
|
||||
class="condition-value" :outlined="true" :dense="true" ></q-select>
|
||||
<q-btn flat round dense icon="more_horiz" size="sm" >
|
||||
<q-menu auto-close anchor="top right">
|
||||
<q-list>
|
||||
<q-item clickable @click="moveUp(prop.node)">
|
||||
<q-item-section avatar><q-icon name="arrow_upward" ></q-icon></q-item-section>
|
||||
<q-item-section >一つ上に移動</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="moveDown(prop.node)">
|
||||
<q-item-section avatar><q-icon name="arrow_downward" ></q-icon></q-item-section>
|
||||
<q-item-section >一つ下に移動</q-item-section>
|
||||
</q-item>
|
||||
<q-separator inset/>
|
||||
<q-item clickable @click="groupMerge(prop.node)" v-if="canMerge(prop.node)">
|
||||
<q-item-section avatar><q-icon name="playlist_add"></q-icon></q-item-section>
|
||||
<q-item-section >グループ化</q-item-section>
|
||||
</q-item>
|
||||
<q-separator inset/>
|
||||
<q-item clickable @click="removeNode(prop.node)">
|
||||
<q-item-section avatar><q-icon name="delete" color="negative"></q-icon></q-item-section>
|
||||
<q-item-section>削除</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</q-tree>
|
||||
<q-tooltip anchor="center middle" v-model="showingCondition" no-parent-event>
|
||||
import { finished } from 'stream';
|
||||
{{ conditionString }}
|
||||
</q-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent,ref,reactive, computed } from 'vue';
|
||||
import { INode,ConditionTree,GroupNode,ConditionNode, LogicalOperator,Operator,NodeType } from '../../types/Conditions';
|
||||
import ConditionObject from './ConditionObject.vue';
|
||||
export default defineComponent( {
|
||||
name: 'NodeCondition',
|
||||
components: {
|
||||
ConditionObject
|
||||
},
|
||||
props:{
|
||||
conditionTree: {
|
||||
type: ConditionTree,
|
||||
default: null
|
||||
},
|
||||
show:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const ticked= ref([]);
|
||||
const showingCondition=ref(false);
|
||||
|
||||
const logicalOperators = computed(()=>{
|
||||
const opts=[];
|
||||
for(const op in LogicalOperator){
|
||||
opts.push(LogicalOperator[op as keyof typeof LogicalOperator]);
|
||||
}
|
||||
return opts;
|
||||
});
|
||||
|
||||
const operators =computed(()=>{
|
||||
const opts=[];
|
||||
for(const op in Operator){
|
||||
opts.push(Operator[op as keyof typeof Operator]);
|
||||
}
|
||||
return opts;
|
||||
});
|
||||
const tree = reactive(props.conditionTree);
|
||||
|
||||
const conditionString = computed(()=>{
|
||||
return tree.buildConditionString(tree.root);
|
||||
});
|
||||
|
||||
const objectValueOptions=(options:any):any[]=>{
|
||||
const opts:any[] =[];
|
||||
Object.keys(options).forEach((key) =>
|
||||
{
|
||||
const opt=options[key];
|
||||
opts.push(opt);
|
||||
});
|
||||
return opts;
|
||||
};
|
||||
|
||||
const addGroup = (parent:GroupNode, logicOp:LogicalOperator) => {
|
||||
if(!parent){
|
||||
parent=tree.root;
|
||||
}
|
||||
tree.addNode(parent,new GroupNode(logicOp,parent));
|
||||
};
|
||||
|
||||
const addCondition = (parent:GroupNode) => {
|
||||
const newNode = new ConditionNode({},Operator.Equal,'',parent);
|
||||
tree.addNode(parent,newNode);
|
||||
};
|
||||
|
||||
const removeNode = (node:INode) => {
|
||||
tree.removeNode(node);
|
||||
};
|
||||
|
||||
const moveUp =(node:INode)=>{
|
||||
tree.moveNode(node,'up');
|
||||
}
|
||||
|
||||
const moveDown =(node:INode)=>{
|
||||
tree.moveNode(node,'down');
|
||||
}
|
||||
|
||||
const getConditionJson=()=>{
|
||||
return tree.toJson();
|
||||
}
|
||||
//JsonからConditionTreeのインスタンスを作成
|
||||
const LoadCondition=()=>{
|
||||
tree.fromJson(conditionString.value);
|
||||
}
|
||||
//グループ化
|
||||
const groupMerge=(node:INode)=>{
|
||||
const checkedNodes:INode[]=[];
|
||||
const checkedIndexs:number[] = ticked.value;
|
||||
checkedIndexs.forEach(index => {
|
||||
const node = tree.findByIndex(index);
|
||||
if(node){
|
||||
checkedNodes.push(node);
|
||||
}
|
||||
});
|
||||
tree.createGroupNode(node,checkedNodes,LogicalOperator.AND);
|
||||
ticked.value=[];
|
||||
}
|
||||
//グループ化可能かをチェックする
|
||||
const canMerge =(node:INode)=>{
|
||||
const checkedIndexs:number[] = ticked.value;
|
||||
const findNode = checkedIndexs.find(index=>node.index===index);
|
||||
console.log("findNode=>",findNode!==undefined,findNode);
|
||||
return findNode!==undefined;
|
||||
}
|
||||
//グループ化解散
|
||||
const splitGroup=(node:INode)=>{
|
||||
tree.dissolveGroupNode(node as GroupNode);
|
||||
ticked.value=[];
|
||||
}
|
||||
|
||||
|
||||
const expanded=computed(()=>tree.getGroups(tree.root));
|
||||
// addCondition(tree.root);
|
||||
|
||||
return {
|
||||
showingCondition,
|
||||
conditionString,
|
||||
tree,
|
||||
ticked,
|
||||
logicalOperators,
|
||||
operators,
|
||||
addGroup,
|
||||
addCondition,
|
||||
removeNode,
|
||||
moveUp,
|
||||
moveDown,
|
||||
LogicalOperator,
|
||||
Operator,
|
||||
NodeType,
|
||||
getConditionJson,
|
||||
LoadCondition,
|
||||
objectValueOptions,
|
||||
expanded,
|
||||
canMerge,
|
||||
groupMerge,
|
||||
splitGroup
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.condition-value{
|
||||
min-width: 200px;
|
||||
max-height: 40px;
|
||||
padding: 2px;
|
||||
}
|
||||
.operator{
|
||||
min-width: 150px;
|
||||
max-height: 40px;
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
font-size: 12pt;
|
||||
}
|
||||
</style>
|
||||
59
frontend/src/components/ConditionObjects.vue
Normal file
59
frontend/src/components/ConditionObjects.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="q-gutter-y-md" style="max-width: 600px;">
|
||||
<q-card >
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
dense
|
||||
class="text-grey"
|
||||
active-color="white"
|
||||
active-bg-color="primary"
|
||||
indicator-color="primary"
|
||||
align="justify"
|
||||
narrow-indicator
|
||||
>
|
||||
<q-tab name="fields" label="フィールド"></q-tab>
|
||||
<q-tab name="vars" label="変数"></q-tab>
|
||||
</q-tabs>
|
||||
|
||||
<q-separator></q-separator>
|
||||
|
||||
<q-tab-panels v-model="tab" animated>
|
||||
<q-tab-panel name="fields">
|
||||
<field-list v-model="selected" type="single" :filter="filter" :appId="appId"></field-list>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="vars" >
|
||||
<variable-list v-model="selected" type="single" :vars="vars"></variable-list>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import FieldList from './FieldList.vue';
|
||||
import VariableList from './VariableList.vue';
|
||||
|
||||
export default {
|
||||
name: 'ConditionObjects',
|
||||
components:{
|
||||
FieldList,
|
||||
VariableList
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
type: String,
|
||||
appId: Number,
|
||||
vars: Array,
|
||||
filter:String
|
||||
},
|
||||
setup(props) {
|
||||
|
||||
return {
|
||||
tab: ref('fields'),
|
||||
selected: ref([])
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -4,6 +4,7 @@
|
||||
style="max-width: 400px"
|
||||
:url="uploadUrl"
|
||||
:label="title"
|
||||
:headers="headers"
|
||||
accept=".csv,.xlsx"
|
||||
v-on:rejected="onRejected"
|
||||
v-on:uploaded="onUploadFinished"
|
||||
@@ -15,7 +16,10 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { createUploaderComponent, useQuasar } from 'quasar';
|
||||
import { useAuthStore } from 'src/stores/useAuthStore';
|
||||
import { ref } from 'vue';
|
||||
const $q=useQuasar();
|
||||
const authStore = useAuthStore();
|
||||
const emit =defineEmits(['uploaded']);
|
||||
/**
|
||||
* ファイルアップロードを拒否する時の処理
|
||||
@@ -67,9 +71,12 @@
|
||||
title: string;
|
||||
uploadUrl:string;
|
||||
}
|
||||
|
||||
const headers = ref([{name:"Authorization",value:'Bearer ' + authStore.token}]);
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
title:"設計書から導入する(csv or excel)",
|
||||
uploadUrl: `${process.env.KAB_BACKEND_URL}createappfromexcel`
|
||||
uploadUrl: `${process.env.KAB_BACKEND_URL}api/v1/createappfromexcel?format=1`
|
||||
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
42
frontend/src/components/DomainSelect.vue
Normal file
42
frontend/src/components/DomainSelect.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table :title="name+'一覧'" :selection="type" v-model:selected="selected" :columns="columns" :rows="rows" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref,onMounted,reactive } from 'vue'
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
export default {
|
||||
name: 'DomainSelect',
|
||||
props: {
|
||||
name: String,
|
||||
type: String
|
||||
},
|
||||
setup() {
|
||||
const columns = [
|
||||
{ name: 'id'},
|
||||
{ name: 'tenantid', required: true,label: 'テナント',align: 'left',field: 'tenantid',sortable: true},
|
||||
{ name: 'name', align: 'center', label: 'ドメイン', field: 'name', sortable: true },
|
||||
{ name: 'url', label: 'URL', field: 'url', sortable: true },
|
||||
{ name: 'kintoneuser', label: 'アカウント', field: 'kintoneuser' }
|
||||
]
|
||||
const rows = reactive([])
|
||||
onMounted( () => {
|
||||
api.get(`api/domains/1`).then(res =>{
|
||||
res.data.forEach((item) =>
|
||||
{
|
||||
rows.push({id:item.id,tenantid:item.tenantid,name:item.name,url:item.url,kintoneuser:item.kintoneuser});
|
||||
}
|
||||
)
|
||||
});
|
||||
});
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
selected: ref([]),
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
43
frontend/src/components/DomainSelector.vue
Normal file
43
frontend/src/components/DomainSelector.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<q-btn-dropdown
|
||||
color="primay"
|
||||
push
|
||||
flat
|
||||
no-caps
|
||||
icon="share"
|
||||
size="md"
|
||||
:label="userStore.currentDomain.domainName"
|
||||
>
|
||||
<q-list>
|
||||
<q-item v-for="domain in domains" :key="domain.domainName"
|
||||
clickable v-close-popup @click="onItemClick(domain)">
|
||||
<q-item-section side>
|
||||
<q-icon name="share" size="sm" color="orange" text-color="white"></q-icon>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{domain.domainName}}</q-item-label>
|
||||
<q-item-label caption>{{domain.kintoneUrl}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts" >
|
||||
import { IDomainInfo } from 'src/types/ActionTypes';
|
||||
import { useAuthStore,IUserState } from 'stores/useAuthStore';
|
||||
import { ref } from 'vue';
|
||||
const userStore = useAuthStore();
|
||||
const domains = ref<IDomainInfo[]>([]);
|
||||
(async ()=>{
|
||||
domains.value = await userStore.getUserDomains();
|
||||
})();
|
||||
|
||||
const onItemClick=(domain:IDomainInfo)=>{
|
||||
console.log(domain);
|
||||
userStore.setCurrentDomain(domain);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
58
frontend/src/components/FieldList.vue
Normal file
58
frontend/src/components/FieldList.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table flat bordered :loading="!isLoaded" row-key="name" :selection="type"
|
||||
:selected="modelValue"
|
||||
@update:selected="$emit('update:modelValue', $event)"
|
||||
:filter="filter"
|
||||
:columns="columns" :rows="rows" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
export default {
|
||||
name: 'FieldList',
|
||||
props: {
|
||||
name: String,
|
||||
type: String,
|
||||
appId: Number,
|
||||
modelValue:Array,
|
||||
filter:String
|
||||
},
|
||||
emits:[
|
||||
'update:modelValue'
|
||||
],
|
||||
setup(props) {
|
||||
const isLoaded = ref(false);
|
||||
const columns = [
|
||||
{ name: 'name', required: true, label: 'フィールド名', align: 'left', field: row => row.name, sortable: true },
|
||||
{ name: 'code', label: 'フィールドコード', align: 'left', field: 'code', sortable: true },
|
||||
{ name: 'type', label: 'フィールドタイプ', align: 'left', field: 'type', sortable: true }
|
||||
]
|
||||
const rows = reactive([]);
|
||||
onMounted(async () => {
|
||||
const res = await api.get('api/v1/appfields', {
|
||||
params: {
|
||||
app: props.appId
|
||||
}
|
||||
});
|
||||
let fields = res.data.properties;
|
||||
console.log(fields);
|
||||
Object.keys(fields).forEach((key) => {
|
||||
const fld = fields[key];
|
||||
rows.push({ name: fld.label, objectType: 'field', ...fld });
|
||||
});
|
||||
isLoaded.value = true;
|
||||
});
|
||||
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
// selected: ref([]),
|
||||
isLoaded
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -1,45 +1,81 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table :title="name+'一覧'" row-key="name" :selection="type" v-model:selected="selected" :columns="columns" :rows="rows" />
|
||||
<div class="q-px-md" style=" min-width: 50vw; max-width: 85vw;">
|
||||
<div v-if="!isLoaded" class="spinner flex flex-center">
|
||||
<q-spinner color="primary" size="3em" />
|
||||
</div>
|
||||
<q-table flat bordered v-else row-key="name" :selection="type" v-model:selected="selected" :columns="columns"
|
||||
:rows="rows" :pagination="pageSetting" :filter="filter" style="max-height: 55vh;"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ref,onMounted,reactive } from 'vue'
|
||||
import { ref, onMounted, reactive, watchEffect } from 'vue'
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
export default {
|
||||
name: 'fieldSelect',
|
||||
props: {
|
||||
name: String,
|
||||
type: String,
|
||||
appId:Number
|
||||
type: {
|
||||
type: String,
|
||||
default: 'single'
|
||||
},
|
||||
appId: Number,
|
||||
not_page: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
selectedFields:{
|
||||
type:Array,
|
||||
default:()=>[]
|
||||
},
|
||||
updateSelects: {
|
||||
type: Function
|
||||
},
|
||||
filter: String,
|
||||
},
|
||||
setup(props) {
|
||||
const columns = [
|
||||
{ name: 'name', required: true,label: 'フィールド名',align: 'left',field: row=>row.name,sortable: true},
|
||||
{ name: 'code', label: 'フィールドコード', align: 'left',field: 'code', sortable: true },
|
||||
{ name: 'type', label: 'フィールドタイプ', align: 'left',field: 'type', sortable: true }
|
||||
]
|
||||
const rows = reactive([])
|
||||
onMounted( () => {
|
||||
api.get('appfields', {
|
||||
params:{
|
||||
app: props.appId
|
||||
}
|
||||
}).then(res =>{
|
||||
let fields = res.data.properties;
|
||||
console.log(fields);
|
||||
Object.keys(fields).forEach((key) =>
|
||||
{
|
||||
rows.push({name:fields[key].label,code:fields[key].code,type:fields[key].type});
|
||||
const isLoaded = ref(false);
|
||||
const columns = [
|
||||
{ name: 'name', required: true, label: 'フィールド名', align: 'left', field: row => row.name, sortable: true },
|
||||
{ name: 'code', label: 'フィールドコード', align: 'left', field: 'code', sortable: true },
|
||||
{ name: 'type', label: 'フィールドタイプ', align: 'left', field: 'type', sortable: true }
|
||||
]
|
||||
const pageSetting = ref({
|
||||
sortBy: 'desc',
|
||||
descending: false,
|
||||
page: 2,
|
||||
rowsPerPage: props.not_page ? 0 : 5
|
||||
// rowsNumber: xx if getting data from a server
|
||||
});
|
||||
const rows = reactive([]);
|
||||
const selected = ref(props.selectedFields && props.selectedFields.length>0?props.selectedFields:[]);
|
||||
|
||||
watchEffect(() => {
|
||||
props.updateSelects(selected);
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await api.get('api/v1/appfields', {
|
||||
params: {
|
||||
app: props.appId
|
||||
}
|
||||
)
|
||||
});
|
||||
let fields = res.data.properties;
|
||||
console.log(fields);
|
||||
Object.keys(fields).forEach((key) => {
|
||||
const fld = fields[key];
|
||||
// rows.push({name:fields[key].label,code:fields[key].code,type:fields[key].type});
|
||||
rows.push({ name: fld.label, ...fld });
|
||||
});
|
||||
isLoaded.value = true;
|
||||
});
|
||||
|
||||
return {
|
||||
columns,
|
||||
rows,
|
||||
selected: ref([]),
|
||||
selected,
|
||||
isLoaded,
|
||||
pageSetting
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export interface Rule{
|
||||
id:number;
|
||||
name:string;
|
||||
condtion:CondtionTree
|
||||
}
|
||||
|
||||
export interface CondtionTree{
|
||||
|
||||
}
|
||||
@@ -1,29 +1,38 @@
|
||||
<template>
|
||||
<div class="q-pa-md q-gutter-sm">
|
||||
<q-dialog :model-value="visible" persistent>
|
||||
<q-card style="min-width: 350px">
|
||||
<!-- <div class="q-pa-md q-gutter-sm" > -->
|
||||
<q-dialog :model-value="visible" persistent bordered >
|
||||
<q-card :style="cardStyle" style=" min-width: 40vw; max-width: 80vw; max-height: 95vh;">
|
||||
<q-toolbar class="bg-grey-4">
|
||||
<q-toolbar-title>{{ name }}</q-toolbar-title>
|
||||
<q-space></q-space>
|
||||
<slot name="toolbar"></slot>
|
||||
<q-btn flat round dense icon="close" @click="CloseDialogue('Cancel')" />
|
||||
</q-toolbar>
|
||||
<q-card-section>
|
||||
<div class="text-h6">{{ name }}選択</div>
|
||||
<!-- <div class="text-h6">{{ name }}</div> -->
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<q-card-section class="q-pt-none" :style="sectionStyle">
|
||||
<slot></slot>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right" class="text-primary">
|
||||
<q-card-actions align="right" class="text-primary q-mt-lg">
|
||||
<q-btn flat label="確定" v-close-popup @click="CloseDialogue('OK')" />
|
||||
<q-btn flat label="キャンセル" v-close-popup @click="CloseDialogue('Cancel')" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import {computed} from 'vue'
|
||||
export default {
|
||||
name: 'showDialog',
|
||||
name: 'ShowDialog',
|
||||
props: {
|
||||
name:String,
|
||||
visible: Boolean,
|
||||
width:String,
|
||||
height:String,
|
||||
minWidth:String,
|
||||
minHeight:String
|
||||
},
|
||||
emits: [
|
||||
'close'
|
||||
@@ -34,8 +43,20 @@ export default {
|
||||
context.emit('close', val);
|
||||
}
|
||||
|
||||
const cardStyle = computed(() => ({
|
||||
minWidth: props.minWidth,
|
||||
width: props.width
|
||||
}));
|
||||
|
||||
const sectionStyle = computed(() => ({
|
||||
height: props.height,
|
||||
minHeight: props.minHeight
|
||||
}));
|
||||
|
||||
return {
|
||||
CloseDialogue
|
||||
CloseDialogue,
|
||||
cardStyle,
|
||||
sectionStyle
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
44
frontend/src/components/VariableList.vue
Normal file
44
frontend/src/components/VariableList.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table flat bordered row-key="name" :selection="type"
|
||||
:selected="modelValue"
|
||||
@update:selected="$emit('update:modelValue', $event)"
|
||||
:columns="columns" :rows="rows" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { ref, reactive, PropType, compile } from 'vue';
|
||||
import {IActionNode,IActionVariable} from '../types/ActionTypes';
|
||||
|
||||
export default {
|
||||
name: 'VariableList',
|
||||
props: {
|
||||
name: String,
|
||||
type: String,
|
||||
vars:{
|
||||
type:Array as PropType<IActionVariable[]>,
|
||||
reqired:true,
|
||||
default:()=>[]
|
||||
},
|
||||
modelValue:Array
|
||||
},
|
||||
emits:[
|
||||
'update:modelValue'
|
||||
],
|
||||
setup(props) {
|
||||
const columns= [
|
||||
{ name: 'actionName', label: 'アクション名',align: 'left',field: 'actionName',sortable: true},
|
||||
{ name: 'displayName', label: '変数表示名', align: 'left',field: 'displayName', sortable: true },
|
||||
{ name: 'name', label: '変数名', align: 'left',field: 'name',required: true, sortable: true }
|
||||
];
|
||||
const rows= props.vars.map((v)=>{
|
||||
return {objectType:'variable',...v};
|
||||
});
|
||||
|
||||
return {
|
||||
columns,
|
||||
rows:reactive(rows)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
29
frontend/src/components/flowEditor/left/ControlPanelC.vue
Normal file
29
frontend/src/components/flowEditor/left/ControlPanelC.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="q-py-md">
|
||||
<q-tree
|
||||
no-connectors
|
||||
selected-color="primary"
|
||||
default-expand-all
|
||||
:nodes="LeftDataBus.root"
|
||||
v-model:selected="flowNames1"
|
||||
node-key="label"
|
||||
>
|
||||
</q-tree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
LeftDataBus,
|
||||
setControlPanelE,
|
||||
} from 'components/flowEditor/left/DataBus';
|
||||
import { ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
|
||||
// 应该在page中用网络请求获取值并初始化组件
|
||||
// 然后在page中执行setControlPane设置databus
|
||||
const store = useFlowEditorStore();
|
||||
const { flowNames1 } = storeToRefs(store);
|
||||
setControlPanelE();
|
||||
</script>
|
||||
72
frontend/src/components/flowEditor/left/DataBus.ts
Normal file
72
frontend/src/components/flowEditor/left/DataBus.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
export const LeftDataBus = reactive<LeftData>({})
|
||||
|
||||
const defaultData = {
|
||||
root: [
|
||||
{
|
||||
label: 'レコードを追加画面',
|
||||
children: [
|
||||
{
|
||||
label: '追加画面表示した時',
|
||||
header: 'rg',
|
||||
value: '1-1',
|
||||
group: 'g1',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
label: '保存をクリックした時',
|
||||
header: 'rg',
|
||||
value: '1-2',
|
||||
group: 'g1',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
label: '保存成功した時',
|
||||
header: 'rg',
|
||||
value: '1-3',
|
||||
group: 'g1',
|
||||
children: []
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'レコード編集画面',
|
||||
},
|
||||
{
|
||||
label: 'レコード詳細画面',
|
||||
},
|
||||
{
|
||||
label: 'レコード一覧画面',
|
||||
},
|
||||
],
|
||||
data: new Map([['g1', '1-1']])
|
||||
}
|
||||
|
||||
export const setControlPanel = (rootData: LeftData) => {
|
||||
const { root: dr, data: dd } = defaultData
|
||||
LeftDataBus.title = rootData.title
|
||||
LeftDataBus.root = rootData.root ?? dr
|
||||
LeftDataBus.data = rootData.data ?? dd
|
||||
}
|
||||
|
||||
export const setControlPanelE = () => {
|
||||
const { root: dr, data: dd } = defaultData
|
||||
// LeftDataBus.title = rootData.title
|
||||
LeftDataBus.root = dr
|
||||
LeftDataBus.data = dd
|
||||
}
|
||||
|
||||
export interface LeftData {
|
||||
title?: string
|
||||
root?: ControlPanelData[]
|
||||
data?: Map<string, string>
|
||||
}
|
||||
|
||||
export interface ControlPanelData {
|
||||
label: string,
|
||||
header?: string,
|
||||
value?: string,
|
||||
group?: string,
|
||||
children?: ControlPanelData[]
|
||||
}
|
||||
42
frontend/src/components/flowEditor/left/ItemSelector.vue
Normal file
42
frontend/src/components/flowEditor/left/ItemSelector.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div
|
||||
class="row"
|
||||
style="
|
||||
border-radius: 2px;
|
||||
box-shadow: rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset,
|
||||
rgba(0, 0, 0, 0.3) 0px 0px 0px 1px;
|
||||
"
|
||||
>
|
||||
<q-icon
|
||||
class="self-center q-ma-sm"
|
||||
name="widgets"
|
||||
color="grey-9"
|
||||
style="font-size: 2em"
|
||||
/>
|
||||
|
||||
<div class="col-7 self-center ellipsis">
|
||||
{{ actName }}
|
||||
</div>
|
||||
|
||||
<div class="self-center">
|
||||
<q-btn
|
||||
outline
|
||||
dense
|
||||
label="変 更"
|
||||
padding="none sm"
|
||||
color="primary"
|
||||
></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default {
|
||||
props: ['actName'],
|
||||
setup(props) {
|
||||
const actName = computed(() => props.actName);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
94
frontend/src/components/left/AppSelector.vue
Normal file
94
frontend/src/components/left/AppSelector.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="row app-box">
|
||||
<q-icon
|
||||
class="self-center q-ma-sm"
|
||||
name="widgets"
|
||||
color="grey-9"
|
||||
style="font-size: 2em"
|
||||
/>
|
||||
<div class="col-7 self-center ellipsis">
|
||||
<a :href="!store.appInfo?'':`${authStore.currentDomain.kintoneUrl}/k/${store.appInfo?.appId}`" target="_blank" title="Kiontoneへ">
|
||||
{{ store.appInfo?.name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="self-center">
|
||||
<q-btn
|
||||
outline
|
||||
dense
|
||||
label="変 更"
|
||||
padding="none sm"
|
||||
color="primary"
|
||||
@click="showAppDialog"
|
||||
></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
<ShowDialog v-model:visible="showSelectApp" name="アプリ選択" @close="closeDg" min-width="50vw" min-height="50vh" >
|
||||
<template v-slot:toolbar>
|
||||
<q-input dense debounce="300" v-model="filter" placeholder="検索" clearable>
|
||||
<template v-slot:before>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<AppSelect ref="appDg" name="アプリ" type="single" :filter="filter"></AppSelect>
|
||||
</ShowDialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent,ref } from 'vue';
|
||||
import {AppInfo} from '../../types/ActionTypes'
|
||||
import ShowDialog from '../../components/ShowDialog.vue';
|
||||
import AppSelect from '../../components/AppSelect.vue';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
import { useAuthStore } from 'src/stores/useAuthStore';
|
||||
export default defineComponent({
|
||||
name: 'AppSelector',
|
||||
emits:[
|
||||
"appSelected"
|
||||
],
|
||||
components:{
|
||||
AppSelect,
|
||||
ShowDialog
|
||||
},
|
||||
setup(props, context) {
|
||||
|
||||
const store = useFlowEditorStore();
|
||||
const authStore=useAuthStore();
|
||||
const appDg = ref();
|
||||
const showSelectApp=ref(false);
|
||||
|
||||
const closeDg=(val :any)=>{
|
||||
showSelectApp.value=false;
|
||||
console.log("Dialog closed->",val);
|
||||
if (val == 'OK') {
|
||||
const data = appDg.value.selected[0];
|
||||
console.log(data);
|
||||
const appInfo={
|
||||
appId:data.id ,
|
||||
name:data.name
|
||||
};
|
||||
store.setApp(appInfo);
|
||||
store.loadFlow();
|
||||
}
|
||||
}
|
||||
const showAppDialog=()=>{
|
||||
showSelectApp.value=true;
|
||||
}
|
||||
return {
|
||||
store,
|
||||
authStore,
|
||||
showSelectApp,
|
||||
showAppDialog,
|
||||
closeDg,
|
||||
appDg,
|
||||
filter:ref('')
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.app-box{
|
||||
border-radius: 2px;
|
||||
box-shadow: rgba(255, 255, 255, 0.1) 0px 0px 0px 1px inset,rgba(0, 0, 0, 0.3) 0px 0px 0px 1px;
|
||||
}
|
||||
</style>
|
||||
151
frontend/src/components/left/EventTree.vue
Normal file
151
frontend/src/components/left/EventTree.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<!-- <div class="q-pa-md q-gutter-sm"> -->
|
||||
<q-tree
|
||||
:nodes="store.eventTree.screens"
|
||||
node-key="eventId"
|
||||
children-key="events"
|
||||
no-connectors
|
||||
v-model:expanded="store.expandedScreen"
|
||||
:dense="true"
|
||||
:ref="tree"
|
||||
>
|
||||
<template v-slot:header-EVENT="prop">
|
||||
<div class="row col items-start no-wrap event-node" @click="onSelected(prop.node)">
|
||||
<q-icon v-if="prop.node.eventId"
|
||||
name="play_circle"
|
||||
:color="prop.node.hasFlow?'green':'grey'"
|
||||
size="16px" class="q-mr-sm">
|
||||
</q-icon>
|
||||
<div class="no-wrap" :class="selectedEvent && prop.node.eventId===selectedEvent.eventId?'selected-node':''">{{ prop.node.label }}</div>
|
||||
<q-space></q-space>
|
||||
<!-- <q-icon v-if="prop.node.hasFlow" name="delete" color="negative" size="16px" class="q-mr-sm"></q-icon> -->
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:header-CHANGE="prop" >
|
||||
<div class="row col items-start no-wrap event-node" >
|
||||
<div class="no-wrap">{{ prop.node.label }}</div>
|
||||
<q-space></q-space>
|
||||
<q-icon name="add_circle" color="primary" size="16px" class="q-mr-sm" @click="addChangeEvent(prop.node)"></q-icon>
|
||||
</div>
|
||||
</template>
|
||||
</q-tree>
|
||||
<show-dialog v-model:visible="showDialog" name="フィールド選択" @close="closeDg" widht="400px">
|
||||
<field-select ref="appDg" name="フィールド" type="single" :appId="store.appInfo?.appId"></field-select>
|
||||
</show-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref } from 'vue';
|
||||
import { IKintoneEvent ,IKintoneEventGroup, IKintoneEventNode, kintoneEvent} from '../../types/KintoneEvents';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
import { ActionFlow, ActionNode, RootAction } from 'src/types/ActionTypes';
|
||||
import ShowDialog from '../ShowDialog.vue';
|
||||
import FieldSelect from '../FieldSelect.vue';
|
||||
import { QTree } from 'quasar';
|
||||
export default defineComponent({
|
||||
name: 'EventTree',
|
||||
components: {
|
||||
ShowDialog,
|
||||
FieldSelect,
|
||||
},
|
||||
setup(props, context) {
|
||||
const appDg = ref();
|
||||
const store = useFlowEditorStore();
|
||||
const showDialog = ref(false);
|
||||
const tree = ref<QTree>();
|
||||
// const eventTree=ref(kintoneEvents);
|
||||
// const selectedFlow = store.currentFlow;
|
||||
|
||||
// const expanded=ref();
|
||||
const selectedEvent = ref<IKintoneEvent|null>(null);
|
||||
const selectedChangeEvent=ref<IKintoneEventGroup|null>(null);
|
||||
const isFieldChange = (node:IKintoneEventNode)=>{
|
||||
return node.header=='EVENT' && node.eventId.indexOf(".change.")>-1;
|
||||
}
|
||||
//フィールド値変更イベント追加
|
||||
const closeDg = (val:string) => {
|
||||
if (val == 'OK') {
|
||||
if(!selectedChangeEvent.value){return;}
|
||||
const field = appDg.value.selected[0];
|
||||
const eventid = `${selectedChangeEvent.value.eventId}.${field.code}`;
|
||||
if(store.eventTree.findEventById(eventid)){
|
||||
return;
|
||||
}
|
||||
selectedChangeEvent.value?.events.push(
|
||||
new kintoneEvent(
|
||||
field.label,
|
||||
eventid,
|
||||
selectedChangeEvent.value.eventId)
|
||||
);
|
||||
tree.value?.expanded?.push(selectedChangeEvent.value.eventId);
|
||||
tree.value?.expandAll();
|
||||
}
|
||||
};
|
||||
const addChangeEvent=(node:IKintoneEventGroup)=>{
|
||||
if(store.appInfo===undefined){
|
||||
return;
|
||||
}
|
||||
selectedChangeEvent.value=node;
|
||||
showDialog.value=true;
|
||||
}
|
||||
const onSelected=(node:IKintoneEvent)=>{
|
||||
if(!node.eventId){
|
||||
return;
|
||||
}
|
||||
selectedEvent.value=node;
|
||||
if(store.appInfo===undefined){
|
||||
return;
|
||||
}
|
||||
const screen = store.eventTree.findEventById(node.parentId);
|
||||
let flow =store.findFlowByEventId(node.eventId);
|
||||
let screenName=screen!==null?screen.label:"";
|
||||
let nodeLabel = node.label;
|
||||
// if(isFieldChange(node)){
|
||||
// screenName=nodeLabel;
|
||||
// nodeLabel=`${node.label}の値を変更したとき`;
|
||||
// }
|
||||
if(flow!==undefined && flow!==null ){
|
||||
store.selectFlow(flow);
|
||||
}else{
|
||||
const root = new RootAction(node.eventId,screenName,nodeLabel)
|
||||
const flow =new ActionFlow(root);
|
||||
store.flows?.push(flow);
|
||||
store.selectFlow(flow);
|
||||
selectedEvent.value.flowData=flow;
|
||||
}
|
||||
};
|
||||
return {
|
||||
// eventTree,
|
||||
// expanded,
|
||||
appDg,
|
||||
tree,
|
||||
showDialog,
|
||||
isFieldChange,
|
||||
onSelected,
|
||||
selectedEvent,
|
||||
addChangeEvent,
|
||||
closeDg,
|
||||
store
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.nowrap{
|
||||
flex-wrap:nowarp;
|
||||
text-wrap:nowarp;
|
||||
}
|
||||
.event-node{
|
||||
cursor:pointer;
|
||||
}
|
||||
.selected-node{
|
||||
color: $primary;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.event-node:hover{
|
||||
background-color: $light-blue-1;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
276
frontend/src/components/main/NodeItem.vue
Normal file
276
frontend/src/components/main/NodeItem.vue
Normal file
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="row justify-center no-wrap" >
|
||||
<div class="row">
|
||||
<q-card class="action-node" :class="nodeStyle" :square="false" @click="onNodeClick" >
|
||||
<q-toolbar class="col" >
|
||||
<div class="text-subtitle2">{{ node.subTitle }}</div>
|
||||
<q-space></q-space>
|
||||
<q-btn flat round dense icon="more_horiz" size="sm" >
|
||||
<q-menu auto-close anchor="top right">
|
||||
<q-list>
|
||||
<q-item clickable v-if="isRoot" @click="copyFlow">
|
||||
<q-item-section avatar><q-icon name="content_copy" ></q-icon></q-item-section>
|
||||
<q-item-section >コピーする</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-if="!isRoot" @click="onEditNode">
|
||||
<q-item-section avatar><q-icon name="edit" ></q-icon></q-item-section>
|
||||
<q-item-section >編集する</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-if="!isRoot" @click="onDeleteNode">
|
||||
<q-item-section avatar><q-icon name="delete" ></q-icon></q-item-section>
|
||||
<q-item-section>削除する</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="onDeleteAllNode">
|
||||
<q-item-section avatar><q-icon name="delete_sweep" ></q-icon></q-item-section>
|
||||
<q-item-section >以下すべて削除する</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</q-toolbar>
|
||||
<q-separator />
|
||||
<q-card-section class="action-title">
|
||||
<div class="row">
|
||||
<span class="text-h7">{{ node.title }}</span>
|
||||
<q-space></q-space>
|
||||
<q-chip color="info" text-color="white" size="0.70rem" v-if="varName(node)" clickable>{{ varName(node) }}</q-chip>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<template v-if="hasBranch">
|
||||
<q-separator />
|
||||
<q-card-actions align="around">
|
||||
<q-btn flat v-for="(point, index) in node.outputPoints" :key="index">
|
||||
{{ point }}
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</template>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="hasBranch">
|
||||
<node-line :action-node="node" @addNode="addNode" :left-columns="leftColumns" :right-columns="rightColumns"></node-line>
|
||||
<div class="row justify-center no-wrap" >
|
||||
<div v-for="(point, index) in node.outputPoints" :key="index" class="column" style="min-width: 300px;">
|
||||
<div class="justify-center" >
|
||||
<node-item v-if="nextNode(point)!==undefined" :key="nextNode(point).id" :isSelected="nextNode(point) === store.activeNode"
|
||||
:actionNode="nextNode(point)" @addNode="addNodeFromItem" @nodeSelected="onNodeSelected" @nodeEdit="onNodeEdit"
|
||||
@deleteNode="onDeleteNodeFromItem" @deleteAllNextNodes="onDeleteAllNextNodes" ></node-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="!hasBranch">
|
||||
<div class="row justify-center no-wrap" >
|
||||
<node-line :action-node="node" @addNode="addNode" ></node-line>
|
||||
</div>
|
||||
<div>
|
||||
<node-item v-if="nextNode('')!==undefined" :key="nextNode('').id" :isSelected="nextNode('') === store.activeNode"
|
||||
:actionNode="nextNode('')" @addNode="addNodeFromItem" @nodeSelected="onNodeSelected" @nodeEdit="onNodeEdit"
|
||||
@deleteNode="onDeleteNodeFromItem" @deleteAllNextNodes="onDeleteAllNextNodes" ></node-item>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref } from 'vue';
|
||||
import { IActionNode, IActionProperty } from '../../types/ActionTypes';
|
||||
import NodeLine, { Direction } from '../main/NodeLine.vue';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
export default defineComponent({
|
||||
name: 'NodeItem',
|
||||
components: {
|
||||
NodeLine
|
||||
},
|
||||
props: {
|
||||
actionNode: {
|
||||
type: Object as () => IActionNode,
|
||||
required: true
|
||||
},
|
||||
isSelected: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
emits: [
|
||||
'addNode',
|
||||
"nodeSelected",
|
||||
"nodeEdit",
|
||||
"deleteNode",
|
||||
"deleteAllNextNodes",
|
||||
"copyFlow"
|
||||
],
|
||||
setup(props, context) {
|
||||
const store = useFlowEditorStore();
|
||||
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
|
||||
const nodeStyle = computed(() => {
|
||||
return {
|
||||
'root-node': props.actionNode.isRoot,
|
||||
'text-white': props.actionNode.isRoot,
|
||||
'selected': props.isSelected && !props.actionNode.isRoot
|
||||
};
|
||||
});
|
||||
|
||||
const nextNode=(point:string)=>{
|
||||
const nextId= props.actionNode.nextNodeIds.get(point);
|
||||
if(!nextId) return undefined;
|
||||
return store.currentFlow?.findNodeById(nextId);
|
||||
}
|
||||
/**
|
||||
* アクションノード追加イベントを
|
||||
* @param point 入力ポイント
|
||||
*/
|
||||
const addNode = (point: string) => {
|
||||
context.emit('addNode', props.actionNode, point);
|
||||
}
|
||||
/**
|
||||
* アクションノード追加イベントを
|
||||
* @param point 入力ポイント
|
||||
*/
|
||||
const addNodeFromItem = (node:IActionNode,point: string) => {
|
||||
context.emit('addNode', node, point);
|
||||
}
|
||||
|
||||
const leftColumns=computed(()=>{
|
||||
if(!props.actionNode.outputPoints || props.actionNode.outputPoints.length<2){
|
||||
return 1;
|
||||
}
|
||||
const leftNode = nextNode(props.actionNode.outputPoints[0]);
|
||||
if(leftNode){
|
||||
return store.currentFlow?.getColumns(leftNode);
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
const rightColumns=computed(()=>{
|
||||
if(!props.actionNode.outputPoints || props.actionNode.outputPoints.length<2){
|
||||
return 1;
|
||||
}
|
||||
const rightNode = nextNode(props.actionNode.outputPoints[1]);
|
||||
if(rightNode){
|
||||
return store.currentFlow?.getColumns(rightNode);
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* ノード選択状態
|
||||
*/
|
||||
const onNodeClick = () => {
|
||||
context.emit('nodeSelected', props.actionNode);
|
||||
}
|
||||
|
||||
|
||||
const onNodeSelected = (node: IActionNode) => {
|
||||
context.emit('nodeSelected', node);
|
||||
}
|
||||
|
||||
const onEditNode=()=>{
|
||||
context.emit('nodeEdit', props.actionNode);
|
||||
}
|
||||
|
||||
const onNodeEdit=(node:IActionNode)=>{
|
||||
context.emit('nodeEdit', node);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ノードを削除する
|
||||
*/
|
||||
const onDeleteNode=()=>{
|
||||
context.emit('deleteNode', props.actionNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* ノードを削除する
|
||||
*/
|
||||
const onDeleteNodeFromItem=(node:IActionNode)=>{
|
||||
context.emit('deleteNode', node);
|
||||
}
|
||||
/**
|
||||
* ノードの以下すべて削除する
|
||||
*/
|
||||
const onDeleteAllNode=()=>{
|
||||
context.emit('deleteAllNextNodes', props.actionNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* ノードの以下すべて削除する
|
||||
*/
|
||||
const onDeleteAllNextNodes=(node:IActionNode)=>{
|
||||
context.emit('deleteAllNextNodes', node);
|
||||
};
|
||||
/**
|
||||
* 変数名取得
|
||||
*/
|
||||
const varName =(node:IActionNode)=>{
|
||||
const prop = node.actionProps.find((prop) => prop.props.name === "verName");
|
||||
return prop?.props.modelValue;
|
||||
};
|
||||
const copyFlow=()=>{
|
||||
context.emit('copyFlow', props.actionNode);
|
||||
}
|
||||
return {
|
||||
store,
|
||||
node: props.actionNode,
|
||||
nextNode,
|
||||
isRoot: props.actionNode.isRoot,
|
||||
hasBranch,
|
||||
nodeStyle,
|
||||
// getMode,
|
||||
addNode,
|
||||
addNodeFromItem,
|
||||
onNodeClick,
|
||||
onNodeSelected,
|
||||
onEditNode,
|
||||
onNodeEdit,
|
||||
onDeleteNode,
|
||||
onDeleteNodeFromItem,
|
||||
onDeleteAllNode,
|
||||
onDeleteAllNextNodes,
|
||||
copyFlow,
|
||||
varName,
|
||||
leftColumns,
|
||||
rightColumns
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.action-node {
|
||||
min-width: 280px !important;
|
||||
}
|
||||
.action-title{
|
||||
max-width: 280px !important;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.line {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.line:after {
|
||||
content: '';
|
||||
background-color: $blue-7;
|
||||
display: block;
|
||||
width: 3px;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 2em;
|
||||
color: $blue-7;
|
||||
}
|
||||
|
||||
.root-node {
|
||||
background-color: $blue-7;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.action-node:not(.root-node):hover{
|
||||
background-color: $light-blue-1;
|
||||
}
|
||||
|
||||
.selected{
|
||||
background-color: $yellow-1;
|
||||
}
|
||||
</style>
|
||||
168
frontend/src/components/main/NodeLine.vue
Normal file
168
frontend/src/components/main/NodeLine.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="row justify-center">
|
||||
<svg class="node-line" style="width:100%" :viewBox="viewBox()">
|
||||
<template v-if="!node.outputPoints || node.outputPoints.length===0" >
|
||||
<polyline :points="points(getMode('')).linePoints" class="line" ></polyline>
|
||||
<text class="add-icon"
|
||||
@click="addNode(node,'')"
|
||||
:x="points(getMode('')).iconPoint.x"
|
||||
:y="points(getMode('')).iconPoint.y"
|
||||
font-family="Arial" font-size="25"
|
||||
text-anchor="middle" dy=".3em" style="cursor: pointer;" >
|
||||
⊕
|
||||
</text>
|
||||
</template>
|
||||
<template v-for="(point, index) in node.outputPoints" :key="index" >
|
||||
<polyline :points="points(getMode(point)).linePoints" class="line" ></polyline>
|
||||
<text class="add-icon"
|
||||
@click="addNode(node,point)"
|
||||
:x="points(getMode(point)).iconPoint.x"
|
||||
:y="points(getMode(point)).iconPoint.y"
|
||||
font-family="Arial" font-size="25"
|
||||
text-anchor="middle" dy=".3em" style="cursor: pointer;" >
|
||||
⊕
|
||||
</text>
|
||||
</template>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent, computed, PropType } from 'vue';
|
||||
import { IActionNode, ActionNode, ActionFlow, RootAction } from '../../types/ActionTypes';
|
||||
export enum Direction {
|
||||
Default = "None",
|
||||
Left = "LEFT",
|
||||
Right = "RIGHT",
|
||||
LeftNotNext = "LEFTNOTNEXT",
|
||||
RightNotNext = "RIGHTNOTNEXT",
|
||||
}
|
||||
export default defineComponent({
|
||||
name: 'NodeLine',
|
||||
props: {
|
||||
actionNode: {
|
||||
type: Object as PropType<IActionNode>,
|
||||
required: true
|
||||
},
|
||||
leftColumns:{
|
||||
type:Number,
|
||||
required:false
|
||||
},
|
||||
rightColumns:{
|
||||
type:Number,
|
||||
required:false
|
||||
}
|
||||
},
|
||||
emits: ['addNode'],
|
||||
setup(props,context) {
|
||||
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
|
||||
const getMode = (point: string):Direction => {
|
||||
if (point === '' || props.actionNode.outputPoints.length === 0) {
|
||||
return Direction.Default;
|
||||
}
|
||||
if (point === props.actionNode.outputPoints[0]) {
|
||||
if (props.actionNode.nextNodeIds.get(point)) {
|
||||
return Direction.Left;
|
||||
} else {
|
||||
return Direction.LeftNotNext;
|
||||
}
|
||||
} else {
|
||||
if (props.actionNode.nextNodeIds.get(point)) {
|
||||
return Direction.Right;
|
||||
} else {
|
||||
return Direction.RightNotNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const points = (mode:Direction) => {
|
||||
let startX ,endX;
|
||||
const leftColumn=props.leftColumns?props.leftColumns:1;
|
||||
const rightColumn=props.rightColumns?props.rightColumns:1;
|
||||
|
||||
switch (mode) {
|
||||
case Direction.Left:
|
||||
startX = leftColumn*300/2.0;
|
||||
endX = ((leftColumn+rightColumn)/2.0 - 0.25)*300;
|
||||
return {
|
||||
linePoints: `${startX}, 60, ${startX}, 40, ${endX}, 40, ${endX}, 0`,
|
||||
iconPoint: { x: endX, y: 20 }
|
||||
};
|
||||
case Direction.Right:
|
||||
startX = ((leftColumn+rightColumn)/2.0 + 0.25)*300;
|
||||
endX = (leftColumn+(rightColumn/2.0))*300;
|
||||
return {
|
||||
linePoints: `${startX}, 0, ${startX}, 40, ${endX}, 40, ${endX}, 60`,
|
||||
iconPoint: { x: startX, y: 20 }
|
||||
};
|
||||
case Direction.LeftNotNext:
|
||||
startX = ((leftColumn+rightColumn)/2.0 - 0.25)*300;
|
||||
return {
|
||||
linePoints: `${startX}, 0, ${startX}, 40`,
|
||||
iconPoint: { x: startX, y: 20 }
|
||||
};
|
||||
case Direction.RightNotNext:
|
||||
startX = ((leftColumn+rightColumn)/2.0 + 0.25)*300;
|
||||
return {
|
||||
linePoints: `${startX}, 0, ${startX}, 40`,
|
||||
iconPoint: { x: startX, y: 20 }
|
||||
};
|
||||
default:
|
||||
return {
|
||||
linePoints: '150, 0, 150, 60',
|
||||
iconPoint: { x: 150, y: 30 }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const addNode=(prveNode:IActionNode,point:string)=>{
|
||||
context.emit('addNode',point);
|
||||
}
|
||||
|
||||
const viewBox=()=>{
|
||||
let columns=0;
|
||||
if(props.leftColumns!==undefined) columns+=props.leftColumns;
|
||||
if(props.rightColumns!==undefined) columns+=props.rightColumns;
|
||||
if(columns===0) columns=1;
|
||||
const width= columns*300;
|
||||
return `0 0 ${width} 60`;
|
||||
};
|
||||
|
||||
return {
|
||||
node: props.actionNode,
|
||||
getMode,
|
||||
hasBranch,
|
||||
points,
|
||||
addNode,
|
||||
viewBox
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.node-line {
|
||||
height: 60px;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.line {
|
||||
stroke: $blue-7;
|
||||
fill: none;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
stroke: $blue-8;
|
||||
fill: $blue-8;
|
||||
font-family: Arial;
|
||||
pointer-events: all;
|
||||
font-size: 2.0em;
|
||||
}
|
||||
|
||||
.add-icon:hover{
|
||||
stroke: $blue-8;
|
||||
fill:$blue-8;
|
||||
font-weight: bold;
|
||||
font-size: 2.4em;
|
||||
}
|
||||
</style>
|
||||
@@ -32,4 +32,3 @@ export interface AppInfo {
|
||||
creator?:User;
|
||||
modifier?:User;
|
||||
}
|
||||
|
||||
|
||||
75
frontend/src/components/right/ActionProperty.vue
Normal file
75
frontend/src/components/right/ActionProperty.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="(item, index) in componentData" :key="index">
|
||||
<component :is="item.component" v-bind="item.props" :connectProps="connectProps" v-model="item.props.modelValue"></component>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent,computed } from 'vue';
|
||||
import InputText from '../right/InputText.vue';
|
||||
import SelectBox from '../right/SelectBox.vue';
|
||||
import DatePicker from '../right/DatePicker.vue';
|
||||
import FieldInput from '../right/FieldInput.vue';
|
||||
import EventSetter from '../right/EventSetter.vue';
|
||||
import { IActionProperty, IProp } from 'src/types/ActionTypes';
|
||||
export default defineComponent({
|
||||
name: 'ActionProperty',
|
||||
components: {
|
||||
InputText,
|
||||
SelectBox,
|
||||
DatePicker,
|
||||
FieldInput,
|
||||
EventSetter
|
||||
},
|
||||
props: {
|
||||
jsonData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
jsonValue:{
|
||||
type: Object,
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
setup(props){
|
||||
const componentData=computed<Array<IActionProperty>>(()=>{
|
||||
return props.jsonData.elements.map((element: any) => {
|
||||
if(props.jsonValue != undefined )
|
||||
{
|
||||
if(props.jsonValue.hasOwnProperty(element.props.name))
|
||||
{
|
||||
element.props.modelValue = props.jsonValue[element.props.name];
|
||||
}
|
||||
else
|
||||
{
|
||||
element.props.modelValue = '';
|
||||
}
|
||||
}
|
||||
return {
|
||||
component: element.component,
|
||||
props: element.props,
|
||||
};
|
||||
});
|
||||
});
|
||||
const connectProps=(props:IProp)=>{
|
||||
const connProps:any={};
|
||||
if(props && "connectProps" in props && props.connectProps!=undefined){
|
||||
for(let connProp of props.connectProps){
|
||||
let targetProp = componentData.value.find((prop)=>prop.props.name===connProp.propName);
|
||||
if(targetProp){
|
||||
connProps[connProp.key]=targetProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return connProps;
|
||||
}
|
||||
|
||||
return{
|
||||
componentData,
|
||||
connectProps
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
235
frontend/src/components/right/AppFieldSelect.vue
Normal file
235
frontend/src/components/right/AppFieldSelect.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div class="q-my-md" v-bind="$attrs">
|
||||
<q-card flat>
|
||||
<q-card-section class="q-pa-none q-my-sm q-mr-md">
|
||||
<!-- <div class=" q-my-none ">App Field Select</div> -->
|
||||
<div class="row q-mb-xs">
|
||||
<div class="text-primary q-mb-xs text-caption">{{ $props.displayName }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="q-mb-xs">{{ selectedField.app?.name || '未選択' }}</div>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<q-btn round flat size="sm" color="primary" icon="search" @click="showDg" />
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-separator />
|
||||
<q-card-section class="q-pa-none q-ma-none">
|
||||
<div style="">
|
||||
<div v-if="selectedField.fields && selectedField.fields.length > 0 ">
|
||||
<q-list bordered>
|
||||
<q-virtual-scroll style="max-height: 160px;" :items="selectedField.fields" separator v-slot="{ item, index }">
|
||||
<q-item :key="index" dense clickable >
|
||||
<q-item-section>
|
||||
<q-item-label>
|
||||
{{ item.label }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-btn round flat size="sm" icon="clear" @click="removeField(index)" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-virtual-scroll>
|
||||
</q-list>
|
||||
</div>
|
||||
<!-- <div v-else class="row q-mt-lg">
|
||||
</div> -->
|
||||
</div>
|
||||
<!-- <q-separator /> -->
|
||||
</q-card-section>
|
||||
<q-card-section class="q-px-none q-py-xs" v-if="selectedField.fields && selectedField.fields.length===0">
|
||||
<div class="row">
|
||||
<div class="text-grey text-caption"> {{ $props.placeholder }}</div>
|
||||
<!-- <q-btn flat color="grey" label="clear" @click="clear" /> -->
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeFieldDialog" ref="fieldDlg">
|
||||
|
||||
<div class="q-mx-md q-mb-lg">
|
||||
<div class="q-mb-xs q-ml-md text-primary">アプリ選択</div>
|
||||
|
||||
<div class="q-pa-md row" style="border: 1px solid rgba(0, 0, 0, 0.12); border-radius: 4px;">
|
||||
<div v-if="!showSelectApp && selectedField.app">{{ selectedField.app?.name }}</div>
|
||||
<q-space />
|
||||
<div>
|
||||
<q-btn outline dense label="選 択" padding="none sm" color="primary" @click="() => {
|
||||
showSelectApp = true;
|
||||
}"></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!showSelectApp && selectedField.app?.name">
|
||||
<div>
|
||||
<div class="row q-mb-md">
|
||||
<!-- <div class="col"> -->
|
||||
<div class="q-mb-xs q-ml-md text-primary">フィールド選択</div>
|
||||
<!-- </div> -->
|
||||
<q-space />
|
||||
<!-- <div class="col"> -->
|
||||
<div class="q-mr-md">
|
||||
<q-input dense debounce="300" v-model="fieldFilter" placeholder="フィールド検索" clearable>
|
||||
<template v-slot:before>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<field-select ref="fieldDlg" name="フィールド" :type="selectType" :updateSelects="updateItems"
|
||||
:appId="selectedField.app?.id" not_page :filter="fieldFilter" :selectedFields="selectedField.fields"></field-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="min-width: 45vw;" v-else>
|
||||
</div>
|
||||
|
||||
</show-dialog>
|
||||
|
||||
<show-dialog v-model:visible="showSelectApp" name="アプリ選択" @close="closeAppDlg">
|
||||
<template v-slot:toolbar>
|
||||
<q-input dense debounce="300" v-model="filter" placeholder="検索" clearable>
|
||||
<template v-slot:before>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<AppSelect ref="appDlg" name="アプリ" type="single" :filter="filter"
|
||||
:updateExternalSelectAppInfo="updateExternalSelectAppInfo"></AppSelect>
|
||||
</show-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watchEffect, computed } from 'vue';
|
||||
import ShowDialog from '../ShowDialog.vue';
|
||||
import FieldSelect from '../FieldSelect.vue';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
import AppSelect from '../AppSelect.vue';
|
||||
interface IApp{
|
||||
id:string,
|
||||
name:string
|
||||
}
|
||||
interface IField {
|
||||
name: string,
|
||||
code: string,
|
||||
type: string
|
||||
}
|
||||
|
||||
interface IAppFields{
|
||||
app?:IApp,
|
||||
fields:IField[]
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs:false,
|
||||
name: 'FieldInput',
|
||||
components: {
|
||||
ShowDialog,
|
||||
FieldSelect,
|
||||
AppSelect,
|
||||
},
|
||||
props: {
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
selectType:{
|
||||
type:String,
|
||||
default:'single'
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const appDlg = ref();
|
||||
const fieldDlg = ref();
|
||||
const show = ref(false);
|
||||
const showSelectApp = ref(false);
|
||||
const selectedField = ref<IAppFields>({
|
||||
app:undefined,
|
||||
fields:[]
|
||||
});
|
||||
if(props.modelValue && "app" in props.modelValue && "fields" in props.modelValue){
|
||||
selectedField.value=props.modelValue as IAppFields;
|
||||
}
|
||||
const store = useFlowEditorStore();
|
||||
|
||||
const isSelected = computed(() => {
|
||||
return selectedField.value !== null && typeof selectedField.value === 'object' && ('app' in selectedField.value)
|
||||
});
|
||||
|
||||
const showDg = () => {
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
selectedField.value ={
|
||||
fields:[]
|
||||
} ;
|
||||
}
|
||||
|
||||
const closeAppDlg = (val: string) => {
|
||||
if (val == 'OK') {
|
||||
selectedField.value.app = appDlg.value.selected[0];
|
||||
selectedField.value.fields=[];
|
||||
showSelectApp.value=false;
|
||||
}
|
||||
};
|
||||
|
||||
const closeFieldDialog=(val:string)=>{
|
||||
if (val == 'OK') {
|
||||
selectedField.value.fields = fieldDlg.value.selected;
|
||||
}
|
||||
};
|
||||
const updateExternalSelectAppInfo = (newAppinfo:IApp) => {
|
||||
// selectedField.value.app = newAppinfo
|
||||
}
|
||||
|
||||
const updateItems = (newFields:IField[]) => {
|
||||
// selectedField.value.fields = newFields
|
||||
}
|
||||
|
||||
const removeField=(index:number)=>{
|
||||
selectedField.value.fields.splice(index,1);
|
||||
}
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', selectedField.value);
|
||||
});
|
||||
|
||||
return {
|
||||
store,
|
||||
appDlg,
|
||||
fieldDlg,
|
||||
show,
|
||||
showDg,
|
||||
closeAppDlg,
|
||||
closeFieldDialog,
|
||||
selectedField,
|
||||
showSelectApp,
|
||||
isSelected,
|
||||
updateExternalSelectAppInfo,
|
||||
filter: ref(),
|
||||
updateItems,
|
||||
clear,
|
||||
fieldFilter: ref(),
|
||||
removeField
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
74
frontend/src/components/right/ColorPicker.vue
Normal file
74
frontend/src/components/right/ColorPicker.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="" v-bind="$attrs">
|
||||
<q-field v-model="color" :label="displayName" labelColor="primary" :clearable="isSelected" stack-label :bottom-slots="!isSelected" >
|
||||
<template v-slot:control>
|
||||
<q-chip text-color="black" color="white" v-if="isSelected">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<q-avatar class="shadow-1" :style="{ background: color }" size="xs"></q-avatar>
|
||||
</div>
|
||||
<div class="col">
|
||||
{{ color }}
|
||||
</div>
|
||||
</div>
|
||||
</q-chip>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-icon name="colorize" class="cursor-pointer" color="primary" >
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-color no-header default-view="palette" v-model="color" />
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
<template v-slot:hint>
|
||||
{{ placeholder }}
|
||||
</template>
|
||||
</q-field>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref,watchEffect } from 'vue';
|
||||
export default defineComponent({
|
||||
inheritAttrs:false,
|
||||
name: 'ColorPicker',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, { emit }) {
|
||||
const color = ref(props.modelValue??"");
|
||||
const isSelected = computed(()=>props.modelValue && props.modelValue!=="");
|
||||
watchEffect(()=>{
|
||||
emit('update:modelValue', color.value);
|
||||
});
|
||||
return {
|
||||
color,
|
||||
isSelected
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
98
frontend/src/components/right/ConditionInput.vue
Normal file
98
frontend/src/components/right/ConditionInput.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div v-bind="$attrs">
|
||||
<q-field v-model="tree" :label="displayName" labelColor="primary" stack-label >
|
||||
<template v-slot:control >
|
||||
<q-card flat class="full-width">
|
||||
<q-card-actions vertical>
|
||||
<q-btn color="grey-3" text-color="black" @click="showDg()">クリックで設定:{{ isSetted?'設定済み':'未設定' }}</q-btn>
|
||||
</q-card-actions>
|
||||
<q-card-section class="text-caption" >
|
||||
<div v-if="!isSetted">{{ placeholder }}</div>
|
||||
<div v-else>{{ conditionString }}</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
</q-field>
|
||||
<condition-editor v-model:show="show" v-model:conditionTree="tree" @closed="onClosed"></condition-editor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref ,watchEffect,computed,reactive} from 'vue';
|
||||
import { ConditionTree,GroupNode,ConditionNode,LogicalOperator,Operator } from 'app/src/types/Conditions';
|
||||
import ConditionEditor from '../ConditionEditor/ConditionEditor.vue'
|
||||
export default defineComponent({
|
||||
name: 'FieldInput',
|
||||
inheritAttrs:false,
|
||||
components: {
|
||||
ConditionEditor
|
||||
},
|
||||
props: {
|
||||
displayName:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, { emit }) {
|
||||
const appDg = ref();
|
||||
const show = ref(false);
|
||||
const tree = reactive(new ConditionTree());
|
||||
if(props.modelValue && props.modelValue!==''){
|
||||
tree.fromJson(props.modelValue);
|
||||
}else{
|
||||
const newNode = new ConditionNode({},Operator.Equal,'',tree.root);
|
||||
tree.addNode(tree.root,newNode);
|
||||
}
|
||||
|
||||
const isSetted=ref(props.modelValue && props.modelValue!=='');
|
||||
|
||||
const conditionString = computed(()=>{
|
||||
return tree.buildConditionString(tree.root);
|
||||
});
|
||||
|
||||
const showDg = () => {
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
const onClosed = (val:string) => {
|
||||
if (val == 'OK') {
|
||||
const conditionJson = tree.toJson();
|
||||
isSetted.value=true;
|
||||
emit('update:modelValue', conditionJson);
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
const conditionJson = tree.toJson();
|
||||
emit('update:modelValue', conditionJson);
|
||||
});
|
||||
|
||||
return {
|
||||
appDg,
|
||||
isSetted,
|
||||
show,
|
||||
showDg,
|
||||
onClosed,
|
||||
tree,
|
||||
conditionString
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
59
frontend/src/components/right/DatePicker.vue
Normal file
59
frontend/src/components/right/DatePicker.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div v-bind="$attrs">
|
||||
<q-input v-model="selectedDate" :label="displayName" :placeholder="placeholder" label-color="primary" mask="date" :rules="['date']" stack-label>
|
||||
<template v-slot:append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date v-model="selectedDate">
|
||||
<div class="row items-center justify-end">
|
||||
<q-btn v-close-popup label="Close" color="primary" flat />
|
||||
</div>
|
||||
</q-date>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref ,watchEffect} from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DatePicker',
|
||||
inheritAttrs:false,
|
||||
props: {
|
||||
displayName:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const selectedDate = ref(props.modelValue);
|
||||
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', selectedDate.value);
|
||||
});
|
||||
|
||||
return {
|
||||
selectedDate
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
83
frontend/src/components/right/EventSetter.vue
Normal file
83
frontend/src/components/right/EventSetter.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div v-bind="$attrs">
|
||||
<q-input :label="displayName" v-model="inputValue" label-color="primary" :placeholder="placeholder" stack-label>
|
||||
<template v-slot:append>
|
||||
<q-btn round dense flat icon="add" @click="addButtonEvent()" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent,ref,watchEffect } from 'vue';
|
||||
import { useFlowEditorStore } from '../../stores/flowEditor';
|
||||
import { IKintoneEventGroup,kintoneEvent } from 'src/types/KintoneEvents';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EventSetter',
|
||||
inheritAttrs:false,
|
||||
props: {
|
||||
displayName:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
connectProps:{
|
||||
type:Object,
|
||||
default:undefined
|
||||
}
|
||||
},
|
||||
|
||||
setup(props , { emit }) {
|
||||
const inputValue = ref(props.modelValue);
|
||||
const store = useFlowEditorStore();
|
||||
const addButtonEvent=()=>{
|
||||
const eventId =store.currentFlow?.getRoot()?.name;
|
||||
if(eventId===undefined){return;}
|
||||
let displayName = inputValue.value;
|
||||
if(props.connectProps!==undefined && "displayName" in props.connectProps){
|
||||
displayName =props.connectProps["displayName"].props.modelValue;
|
||||
}
|
||||
const customButtonId=`${eventId}.customButtonClick`;
|
||||
const findedEvent = store.eventTree.findEventById(customButtonId);
|
||||
if(findedEvent && "events" in findedEvent){
|
||||
const customEvents = findedEvent as IKintoneEventGroup;
|
||||
const addEventId = customButtonId+"." + inputValue.value;
|
||||
if(store.eventTree.findEventById(addEventId)){
|
||||
return;
|
||||
}
|
||||
customEvents.events.push(
|
||||
new kintoneEvent(
|
||||
displayName,
|
||||
addEventId,
|
||||
customButtonId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', inputValue.value);
|
||||
});
|
||||
|
||||
return {
|
||||
inputValue,
|
||||
addButtonEvent
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
101
frontend/src/components/right/FieldInput.vue
Normal file
101
frontend/src/components/right/FieldInput.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div v-bind="$attrs">
|
||||
<q-field v-model="selectedField" :label="displayName" labelColor="primary" :clearable="isSelected" stack-label
|
||||
:bottom-slots="!isSelected">
|
||||
<template v-slot:control>
|
||||
<q-chip color="primary" text-color="white" v-if="isSelected">
|
||||
{{ selectedField.name }}
|
||||
</q-chip>
|
||||
</template>
|
||||
<!-- <template v-slot:hint v-if="isSelected">
|
||||
<div> 項目コード:<q-chip size="sm" outline color="secondary" text-color="white">{{selectedField.code}}</q-chip></div>
|
||||
</template> -->
|
||||
<template v-slot:hint v-if="!isSelected">
|
||||
{{ placeholder }}
|
||||
</template>
|
||||
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" class="cursor-pointer" color="primary" @click="showDg" />
|
||||
</template>
|
||||
</q-field>
|
||||
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeDg" widht="400px">
|
||||
<field-select ref="appDg" name="フィールド" type="single" :appId="store.appInfo?.appId"></field-select>
|
||||
</show-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watchEffect, computed } from 'vue';
|
||||
import ShowDialog from '../ShowDialog.vue';
|
||||
import FieldSelect from '../FieldSelect.vue';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
interface IField {
|
||||
name: string,
|
||||
code: string,
|
||||
type: string
|
||||
}
|
||||
export default defineComponent({
|
||||
name: 'FieldInput',
|
||||
inheritAttrs:false,
|
||||
components: {
|
||||
ShowDialog,
|
||||
FieldSelect,
|
||||
},
|
||||
props: {
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, { emit }) {
|
||||
const appDg = ref();
|
||||
const show = ref(false);
|
||||
const selectedField = ref(props.modelValue);
|
||||
const store = useFlowEditorStore();
|
||||
const isSelected = computed(() => {
|
||||
return selectedField.value !== null && typeof selectedField.value === 'object' && ('name' in selectedField.value)
|
||||
});
|
||||
|
||||
const showDg = () => {
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
const closeDg = (val: string) => {
|
||||
if (val == 'OK') {
|
||||
selectedField.value = appDg.value.selected[0];
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', selectedField.value);
|
||||
});
|
||||
|
||||
return {
|
||||
store,
|
||||
appDg,
|
||||
show,
|
||||
showDg,
|
||||
closeDg,
|
||||
selectedField,
|
||||
isSelected
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
79
frontend/src/components/right/InputText.vue
Normal file
79
frontend/src/components/right/InputText.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div v-bind="$attrs">
|
||||
<q-input :label="displayName" v-model="inputValue" label-color="primary"
|
||||
:placeholder="placeholder" stack-label
|
||||
:rules="rulesExp"
|
||||
:maxlength="maxLength"
|
||||
>
|
||||
<template v-slot:append v-if="hint !== ''">
|
||||
<q-icon name="help" size="22px" color="blue-8">
|
||||
<q-tooltip class="bg-yellow-2 text-black shadow-4" anchor="bottom right">
|
||||
<div class="hint-text" v-html="hint" />
|
||||
</q-tooltip>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { kMaxLength } from 'buffer';
|
||||
import { defineComponent, ref, watchEffect } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InputText',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
maxLength:{
|
||||
type: Number,
|
||||
default:undefined
|
||||
},
|
||||
//例:[val=>!!val ||'入力してください']
|
||||
rules:{
|
||||
type:String,
|
||||
default:undefined
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, { emit }) {
|
||||
const inputValue = ref(props.modelValue);
|
||||
const rulesExp = props.rules===undefined?null : eval(props.rules);
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', inputValue.value);
|
||||
});
|
||||
|
||||
return {
|
||||
inputValue,
|
||||
showhint: ref(false),
|
||||
rulesExp
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.hint-text {
|
||||
white-space: always;
|
||||
max-width: 450px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
</style>
|
||||
49
frontend/src/components/right/MuiltInputText.vue
Normal file
49
frontend/src/components/right/MuiltInputText.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div v-bind="$attrs">
|
||||
<q-input :label="displayName" label-color="primary" v-model="inputValue" :placeholder="placeholder" autogrow
|
||||
stack-label />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watchEffect } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MuiltInputText',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, { emit }) {
|
||||
const inputValue = ref(props.modelValue);
|
||||
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', inputValue.value);
|
||||
});
|
||||
|
||||
return {
|
||||
inputValue,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
87
frontend/src/components/right/NumInput.vue
Normal file
87
frontend/src/components/right/NumInput.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="" v-bind="$attrs">
|
||||
<q-input v-model.number="numValue" type="number" :label="displayName" label-color="primary" stack-label bottom-slots
|
||||
:min="min"
|
||||
:max="max"
|
||||
:rules="rulesExp"
|
||||
>
|
||||
<template v-slot:hint>
|
||||
{{ placeholder }}
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, watchEffect } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'NumInput',
|
||||
inheritAttrs:false,
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
min:{
|
||||
type:Number,
|
||||
default:undefined
|
||||
},
|
||||
max:{
|
||||
type:Number,
|
||||
default:undefined
|
||||
},
|
||||
//[val=>!!val ||'数値を入力してください',val=>val<=100 && val>=1 || '1-100の範囲内の数値を入力してください']
|
||||
rules:{
|
||||
type:String,
|
||||
default:undefined
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number , String],
|
||||
default: undefined
|
||||
},
|
||||
},
|
||||
|
||||
setup(props, { emit }) {
|
||||
const numValue = ref(props.modelValue);
|
||||
const rulesExp = props.rules===undefined?null : eval(props.rules);
|
||||
const isError = computed(()=>{
|
||||
const val = numValue.value;
|
||||
if (val === undefined) {
|
||||
return false;
|
||||
}
|
||||
const numVal = typeof val === "string" ? parseInt(val) : val;
|
||||
// Ensure parsed value is a valid number
|
||||
if (isNaN(numVal)) {
|
||||
return true;
|
||||
}
|
||||
// Check against min and max boundaries, if defined
|
||||
if ((props.min !== undefined && numVal < props.min) || (props.max !== undefined && numVal > props.max)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
watchEffect(()=>{
|
||||
emit("update:modelValue",numValue.value);
|
||||
});
|
||||
return {
|
||||
numValue,
|
||||
rulesExp
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
72
frontend/src/components/right/PropertyList.vue
Normal file
72
frontend/src/components/right/PropertyList.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="(item, index) in properties" :key="index" >
|
||||
<component :is="item.component" v-bind="item.props" :connectProps="connectProps(item.props)" v-model="item.props.modelValue"></component>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
/**
|
||||
* プロパティ属性設定生成する
|
||||
*/
|
||||
import { PropType, defineComponent,ref } from 'vue';
|
||||
import InputText from '../right/InputText.vue';
|
||||
import SelectBox from '../right/SelectBox.vue';
|
||||
import DatePicker from '../right/DatePicker.vue';
|
||||
import FieldInput from '../right/FieldInput.vue';
|
||||
import AppFieldSelect from './AppFieldSelect.vue';
|
||||
import MuiltInputText from '../right/MuiltInputText.vue';
|
||||
import ConditionInput from '../right/ConditionInput.vue';
|
||||
import EventSetter from '../right/EventSetter.vue';
|
||||
import ColorPicker from './ColorPicker.vue';
|
||||
import NumInput from './NumInput.vue';
|
||||
import { IActionNode,IActionProperty,IProp } from 'src/types/ActionTypes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PropertyList',
|
||||
components: {
|
||||
InputText,
|
||||
SelectBox,
|
||||
DatePicker,
|
||||
FieldInput,
|
||||
AppFieldSelect,
|
||||
MuiltInputText,
|
||||
ConditionInput,
|
||||
EventSetter,
|
||||
ColorPicker,
|
||||
NumInput
|
||||
},
|
||||
props: {
|
||||
nodeProps: {
|
||||
type: Object as PropType<Array<IActionProperty>>,
|
||||
required: true,
|
||||
},
|
||||
jsonValue:{
|
||||
type: Object,
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
const properties=ref(props.nodeProps);
|
||||
const connectProps=(props:IProp)=>{
|
||||
const connProps:any={};
|
||||
if(props && "connectProps" in props && props.connectProps!=undefined){
|
||||
for(let connProp of props.connectProps){
|
||||
let targetProp = properties.value.find((prop)=>prop.props.name===connProp.propName);
|
||||
if(targetProp){
|
||||
connProps[connProp.key]=targetProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return connProps;
|
||||
}
|
||||
return {
|
||||
properties,
|
||||
connectProps
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
80
frontend/src/components/right/PropertyPanel.vue
Normal file
80
frontend/src/components/right/PropertyPanel.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="q-pa-md q-gutter-sm">
|
||||
<q-drawer
|
||||
side="right"
|
||||
:show-if-above="false"
|
||||
bordered
|
||||
:width="301"
|
||||
:breakpoint="500"
|
||||
class="bg-grey-3"
|
||||
:model-value="showPanel"
|
||||
elevated
|
||||
overlay
|
||||
>
|
||||
<q-card class="column" style="max-width: 300px;min-height: 100%">
|
||||
<q-card-section>
|
||||
<div class="text-h6">{{ actionNode?.subTitle }}:設定</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="col q-pt-none">
|
||||
<property-list :node-props="actionProps" v-if="showPanel" ></property-list>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="bg-white text-teal">
|
||||
<q-btn flat label="キャンセル" @click="cancel" outline dense padding="none sm" color="primary"/>
|
||||
<q-btn flat label="更新" @click="save" outline dense padding="none sm" color="primary" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { ref,defineComponent, PropType ,watchEffect} from 'vue'
|
||||
import PropertyList from 'components/right/PropertyList.vue';
|
||||
import { IActionNode } from 'src/types/ActionTypes';
|
||||
export default defineComponent({
|
||||
name: 'PropertyPanel',
|
||||
components: {
|
||||
PropertyList
|
||||
},
|
||||
props: {
|
||||
actionNode:{
|
||||
type:Object as PropType<IActionNode>,
|
||||
required:true
|
||||
},
|
||||
drawerRight:{
|
||||
type:Boolean,
|
||||
required:true
|
||||
}
|
||||
},
|
||||
emits: [
|
||||
'update:drawerRight'
|
||||
],
|
||||
setup(props,{emit}) {
|
||||
const showPanel =ref(props.drawerRight);
|
||||
const actionProps =ref(props.actionNode?.actionProps);
|
||||
watchEffect(() => {
|
||||
showPanel.value = props.drawerRight;
|
||||
actionProps.value= props.actionNode?.actionProps;
|
||||
});
|
||||
|
||||
const cancel = async() =>{
|
||||
showPanel.value = false;
|
||||
emit('update:drawerRight',false )
|
||||
}
|
||||
|
||||
const save = async () =>{
|
||||
showPanel.value=false;
|
||||
emit('update:drawerRight',false )
|
||||
}
|
||||
|
||||
return {
|
||||
cancel,
|
||||
save,
|
||||
actionProps,
|
||||
showPanel
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
43
frontend/src/components/right/SelectBox.vue
Normal file
43
frontend/src/components/right/SelectBox.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div v-bind="$attrs">
|
||||
<q-select v-model="selectedValue" :label="displayName" :options="options"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent,ref,watchEffect } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SelectBox',
|
||||
inheritAttrs:false,
|
||||
props: {
|
||||
displayName:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const selectedValue = ref(props.modelValue);
|
||||
|
||||
watchEffect(() => {
|
||||
emit('update:modelValue', selectedValue.value);
|
||||
});
|
||||
|
||||
return {
|
||||
selectedValue
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
22
frontend/src/control/auth.ts
Normal file
22
frontend/src/control/auth.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
export class Auth
|
||||
{
|
||||
|
||||
async login(user:string,pwd:string):Promise<boolean>
|
||||
{
|
||||
const params = new URLSearchParams();
|
||||
params.append('username', user);
|
||||
params.append('password', pwd);
|
||||
try{
|
||||
const result = await api.post(`api/token`,params);
|
||||
console.info(result);
|
||||
localStorage.setItem('Token', result.data.access_token);
|
||||
return true;
|
||||
}catch(e)
|
||||
{
|
||||
console.info(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
frontend/src/control/flowctrl.ts
Normal file
57
frontend/src/control/flowctrl.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { api } from 'boot/axios';
|
||||
import { ActionFlow } from 'src/types/ActionTypes';
|
||||
|
||||
export class FlowCtrl
|
||||
{
|
||||
|
||||
async getFlows(appId:string):Promise<ActionFlow[]>
|
||||
{
|
||||
const flows:ActionFlow[]=[];
|
||||
try{
|
||||
const result = await api.get(`api/flows/${appId}`);
|
||||
//console.info(result.data);
|
||||
if(!result.data || !Array.isArray(result.data)){
|
||||
return [];
|
||||
}
|
||||
|
||||
for(const flow of result.data){
|
||||
flows.push(ActionFlow.fromJSON(flow.content));
|
||||
}
|
||||
return flows;
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
return flows;
|
||||
}
|
||||
}
|
||||
|
||||
async SaveFlow(jsonData:any):Promise<boolean>
|
||||
{
|
||||
const result = await api.post('api/flow',jsonData);
|
||||
console.info(result.data)
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* フローを更新する
|
||||
* @param jsonData
|
||||
* @returns
|
||||
*/
|
||||
async UpdateFlow(jsonData:any):Promise<boolean>
|
||||
{
|
||||
const result = await api.put('api/flow/' + jsonData.flowid,jsonData);
|
||||
console.info(result.data)
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* デプロイ
|
||||
* @param appid
|
||||
* @returns
|
||||
*/
|
||||
async depoly(appid:string):Promise<boolean>
|
||||
{
|
||||
const result = await api.post(`api/v1/createjstokintone?app=${appid}`);
|
||||
console.info(result.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1,25 @@
|
||||
// app global css in SCSS form
|
||||
::-webkit-scrollbar {
|
||||
height: 12px;
|
||||
width: 14px;
|
||||
background: transparent;
|
||||
z-index: 12;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
width: 10px;
|
||||
background-color: #c1c1c1;
|
||||
border-radius: 10px;
|
||||
z-index: 12;
|
||||
border: 4px solid rgba(0, 0, 0, 0);
|
||||
background-clip: padding-box;
|
||||
transition: background-color .32s ease-in-out;
|
||||
margin: 4px;
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #c1c1c1;
|
||||
}
|
||||
|
||||
@@ -11,14 +11,16 @@
|
||||
@click="toggleLeftDrawer"
|
||||
/>
|
||||
<q-toolbar-title>
|
||||
Kintone App Builder
|
||||
<q-badge align="top" outline>V{{ env.version }}</q-badge>
|
||||
{{ productName }}
|
||||
<q-badge align="top" outline>V{{ version }}</q-badge>
|
||||
</q-toolbar-title>
|
||||
<domain-selector></domain-selector>
|
||||
<q-btn flat round dense icon="logout" @click="authStore.logout()"/>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
|
||||
<q-drawer
|
||||
v-model="leftDrawerOpen"
|
||||
:model-value="authStore.toggleLeftDrawer"
|
||||
:show-if-above="false"
|
||||
bordered
|
||||
>
|
||||
@@ -26,7 +28,7 @@
|
||||
<q-item-label
|
||||
header
|
||||
>
|
||||
Essential Links
|
||||
関連リンク
|
||||
</q-item-label>
|
||||
|
||||
<EssentialLink
|
||||
@@ -46,6 +48,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import EssentialLink, { EssentialLinkProps } from 'components/EssentialLink.vue';
|
||||
import DomainSelector from 'components/DomainSelector.vue';
|
||||
import { useAuthStore } from 'stores/useAuthStore';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const essentialLinks: EssentialLinkProps[] = [
|
||||
{
|
||||
@@ -56,10 +62,17 @@ const essentialLinks: EssentialLinkProps[] = [
|
||||
target:'_self'
|
||||
},
|
||||
{
|
||||
title: 'ルールエディター',
|
||||
caption: 'rule',
|
||||
icon: 'rule',
|
||||
link: '/#/ruleEditor',
|
||||
title: 'フローエディター',
|
||||
caption: 'flowChart',
|
||||
icon: 'account_tree',
|
||||
link: '/#/FlowChart',
|
||||
target:'_self'
|
||||
},
|
||||
{
|
||||
title: '条件エディター',
|
||||
caption: 'condition',
|
||||
icon: 'tune',
|
||||
link: '/#/condition',
|
||||
target:'_self'
|
||||
},
|
||||
{
|
||||
@@ -138,11 +151,10 @@ const essentialLinks: EssentialLinkProps[] = [
|
||||
}
|
||||
];
|
||||
|
||||
const leftDrawerOpen = ref(false)
|
||||
|
||||
const env=process.env;
|
||||
const version = process.env.version;
|
||||
const productName = process.env.productName;
|
||||
|
||||
function toggleLeftDrawer() {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||
authStore.toggleLeftMenu();
|
||||
}
|
||||
</script>
|
||||
|
||||
274
frontend/src/pages/FlowChart.vue
Normal file
274
frontend/src/pages/FlowChart.vue
Normal file
@@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<q-page>
|
||||
<q-layout container class="absolute-full shadow-2 rounded-borders">
|
||||
<div class="q-pa-sm q-gutter-sm ">
|
||||
<q-drawer side="left" :overlay="true" bordered v-model="drawerLeft" :show-if-above="false" elevated>
|
||||
<div class="flex-center fixed-top app-selector">
|
||||
<AppSelector />
|
||||
</div>
|
||||
|
||||
<div class="flex-center absolute-full" style="padding-top:65px;padding-left:15px;padding-right:15px;">
|
||||
<q-scroll-area class="fit" :horizontal-thumb-style="{ opacity: '0' }">
|
||||
<EventTree />
|
||||
</q-scroll-area>
|
||||
</div>
|
||||
|
||||
<div class="flex-center fixed-bottom bg-grey-3 q-pa-md row ">
|
||||
<q-btn color="secondary" glossy label="デプロイ" @click="onDeploy" icon="sync" :loading="deployLoading" />
|
||||
<q-space></q-space>
|
||||
<q-btn color="primary" label="保存" @click="onSaveFlow" icon="save" :loading="saveLoading" />
|
||||
</div>
|
||||
</q-drawer>
|
||||
</div>
|
||||
<q-btn flat dense round
|
||||
:icon="drawerLeft?'keyboard_double_arrow_left':'keyboard_double_arrow_right'"
|
||||
:style="[drawerLeft?{'left':'300px'}:{'left':'0px'}]"
|
||||
@click="drawerLeft=!drawerLeft" class="expand" />
|
||||
<div class="q-pa-md q-gutter-sm" :style="{minWidth: minPanelWidth}">
|
||||
<div class="flowchart" v-if="store.currentFlow" :style="[drawerLeft?{paddingLeft:'300px'}:{}]">
|
||||
<node-item v-if="rootNode!==undefined" :key="rootNode.id" :isSelected="rootNode === store.activeNode"
|
||||
:actionNode="rootNode" @addNode="addNode" @nodeSelected="onNodeSelected" @nodeEdit="onNodeEdit"
|
||||
@deleteNode="onDeleteNode" @deleteAllNextNodes="onDeleteAllNextNodes" @copyFlow="onCopyFlow"></node-item>
|
||||
</div>
|
||||
</div>
|
||||
<PropertyPanel :actionNode="store.activeNode" v-model:drawerRight="drawerRight"></PropertyPanel>
|
||||
</q-layout>
|
||||
<ShowDialog v-model:visible="showAddAction" name="アクション" @close="closeDg" min-width="500px" min-height="500px">
|
||||
<template v-slot:toolbar>
|
||||
<q-input dense debounce="200" v-model="filter" placeholder="検索" clearable>
|
||||
<template v-slot:before>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<action-select ref="appDg" name="model" :filter="filter" type="single"></action-select>
|
||||
</ShowDialog>
|
||||
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { IActionNode, ActionNode, IActionFlow, ActionFlow, RootAction, IActionProperty } from 'src/types/ActionTypes';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
import { useAuthStore } from 'stores/useAuthStore';
|
||||
|
||||
import NodeItem from 'src/components/main/NodeItem.vue';
|
||||
import ShowDialog from 'components/ShowDialog.vue';
|
||||
import ActionSelect from 'components/ActionSelect.vue';
|
||||
import PropertyPanel from 'components/right/PropertyPanel.vue';
|
||||
import AppSelector from 'components/left/AppSelector.vue';
|
||||
import EventTree from 'components/left/EventTree.vue';
|
||||
import { FlowCtrl } from '../control/flowctrl';
|
||||
import { useQuasar } from 'quasar';
|
||||
const deployLoading = ref(false);
|
||||
const saveLoading = ref(false);
|
||||
const drawerLeft = ref(false);
|
||||
const $q = useQuasar();
|
||||
const store = useFlowEditorStore();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const appDg = ref();
|
||||
const prevNodeIfo = ref({
|
||||
prevNode: {} as IActionNode,
|
||||
inputPoint: ""
|
||||
});
|
||||
// const refFlow = ref<ActionFlow|null>(null);
|
||||
const showAddAction = ref(false);
|
||||
const drawerRight = ref(false);
|
||||
const filter=ref("");
|
||||
const model = ref("");
|
||||
const addActionNode = (action: IActionNode) => {
|
||||
// refFlow.value?.actionNodes.push(action);
|
||||
store.currentFlow?.actionNodes.push(action);
|
||||
}
|
||||
const rootNode = computed(()=>{
|
||||
return store.currentFlow?.getRoot();
|
||||
});
|
||||
const minPanelWidth=computed(()=>{
|
||||
const root = store.currentFlow?.getRoot();
|
||||
if(store.currentFlow && root){
|
||||
return store.currentFlow?.getColumns(root) * 300 + 'px';
|
||||
}else{
|
||||
return "300px";
|
||||
}
|
||||
});
|
||||
|
||||
const addNode = (node: IActionNode, inputPoint: string) => {
|
||||
if (drawerRight.value) {
|
||||
drawerRight.value = false;
|
||||
}
|
||||
showAddAction.value = true;
|
||||
prevNodeIfo.value.prevNode = node;
|
||||
prevNodeIfo.value.inputPoint = inputPoint;
|
||||
}
|
||||
|
||||
const onNodeSelected = (node: IActionNode) => {
|
||||
//右パネルが開いている場合、自動閉じる
|
||||
if (drawerRight.value && store.activeNode?.id !== node.id) {
|
||||
drawerRight.value = false;
|
||||
}
|
||||
store.setActiveNode(node);
|
||||
}
|
||||
|
||||
const onNodeEdit = (node: IActionNode) => {
|
||||
store.setActiveNode(node);
|
||||
drawerRight.value = true;
|
||||
}
|
||||
|
||||
const onDeleteNode = (node: IActionNode) => {
|
||||
if (!store.currentFlow) return;
|
||||
//右パネルが開いている場合、自動閉じる
|
||||
if (drawerRight.value && store.activeNode?.id === node.id) {
|
||||
drawerRight.value = false;
|
||||
}
|
||||
store.currentFlow?.removeNode(node);
|
||||
}
|
||||
|
||||
const onDeleteAllNextNodes = (node: IActionNode) => {
|
||||
if (!store.currentFlow) return;
|
||||
//右パネルが開いている場合、自動閉じる
|
||||
if (drawerRight.value) {
|
||||
drawerRight.value = false;
|
||||
}
|
||||
store.currentFlow?.removeAllNext(node.id);
|
||||
}
|
||||
const closeDg = (val: any) => {
|
||||
console.log("Dialog closed->", val);
|
||||
if (val == 'OK') {
|
||||
const data = appDg.value.selected[0];
|
||||
const actionProps = JSON.parse(data.property);
|
||||
const outputPoint = JSON.parse(data.outputPoints);
|
||||
const action = new ActionNode(data.name, data.desc, "", outputPoint, actionProps);
|
||||
store.currentFlow?.addNode(action, prevNodeIfo.value.prevNode, prevNodeIfo.value.inputPoint);
|
||||
}
|
||||
}
|
||||
/*
|
||||
*フローのデータをコピーする
|
||||
*/
|
||||
const onCopyFlow = () => {
|
||||
if (navigator.clipboard) {
|
||||
const jsonData =JSON.stringify(store.currentFlow) ;
|
||||
navigator.clipboard.writeText(jsonData).then(() => {
|
||||
console.log('Text successfully copied to clipboard');
|
||||
},
|
||||
(err) => {
|
||||
console.error('Error in copying text: ', err);
|
||||
});
|
||||
} else {
|
||||
console.log('Clipboard API not available');
|
||||
}
|
||||
};
|
||||
/**
|
||||
* デプロイ
|
||||
*/
|
||||
const onDeploy = async () => {
|
||||
if (store.appInfo === undefined || store.flows?.length === 0) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
caption: "エラー",
|
||||
message: `設定されたフローがありません。`
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
deployLoading.value = true;
|
||||
await store.deploy();
|
||||
deployLoading.value = false;
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
caption: "通知",
|
||||
message: `デプロイを成功しました。`
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
deployLoading.value = false;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
caption: "エラー",
|
||||
message: `デプロイが失敗しました。`
|
||||
})
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const onSaveFlow = async () => {
|
||||
const targetFlow = store.selectedFlow;
|
||||
if (targetFlow === undefined) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
caption: "エラー",
|
||||
message: `編集中のフローがありません。`
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
saveLoading.value = true;
|
||||
await store.saveFlow(targetFlow);
|
||||
saveLoading.value = false;
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
caption: "通知",
|
||||
message: `${targetFlow.getRoot()?.subTitle}のフロー設定を保存しました。`
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
saveLoading.value = false;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
caption: "エラー",
|
||||
message: `${targetFlow.getRoot()?.subTitle}のフローの設定の保存が失敗しました。`
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
drawerLeft.value = true;
|
||||
if (store.appInfo === undefined) return;
|
||||
const flowCtrl = new FlowCtrl();
|
||||
const actionFlows = await flowCtrl.getFlows(store.appInfo?.appId);
|
||||
if (actionFlows && actionFlows.length > 0) {
|
||||
store.setFlows(actionFlows);
|
||||
}
|
||||
if (actionFlows && actionFlows.length == 1) {
|
||||
store.selectFlow(actionFlows[0]);
|
||||
}
|
||||
const root = actionFlows[0].getRoot();
|
||||
if (root) {
|
||||
store.setActiveNode(root);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
authStore.toggleLeftMenu();
|
||||
fetchData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.app-selector {
|
||||
padding: 15px;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.flowchart {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.flow-toolbar {
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
.event-tree .q-drawer {
|
||||
top: 50px;
|
||||
z-index: 999;
|
||||
}
|
||||
.expand{
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 50%;
|
||||
z-index: 9999;
|
||||
}
|
||||
</style>
|
||||
102
frontend/src/pages/FlowChartTest.vue
Normal file
102
frontend/src/pages/FlowChartTest.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<q-page>
|
||||
|
||||
<div class="flowchart">
|
||||
<node-item v-for="(node,) in refFlow.actionNodes" :key="node.id"
|
||||
:isSelected="node===state.activeNode" :actionNode="node"
|
||||
@addNode="addNode"
|
||||
@nodeSelected="onNodeSelected"
|
||||
@nodeEdit="onNodeEdit"
|
||||
@deleteNode="onDeleteNode"
|
||||
@deleteAllNextNodes="onDeleteAllNextNodes"
|
||||
></node-item>
|
||||
</div>
|
||||
</q-page>
|
||||
<PropertyPanel :actionNode="state.activeNode" v-model:drawerRight="drawerRight"></PropertyPanel>
|
||||
<show-dialog v-model:visible="showAddAction" name="アクション" @close="closeDg" width="350px">
|
||||
<action-select ref="appDg" name="アクション" type="single"></action-select>
|
||||
</show-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref,reactive,computed} from 'vue';
|
||||
import {IActionNode, ActionNode, IActionFlow, ActionFlow,RootAction, IActionProperty } from 'src/types/ActionTypes';
|
||||
import NodeItem from 'src/components/main/NodeItem.vue';
|
||||
import ShowDialog from 'components/ShowDialog.vue';
|
||||
import ActionSelect from 'components/ActionSelect.vue';
|
||||
import PropertyPanel from 'components/right/PropertyPanel.vue';
|
||||
|
||||
|
||||
const rootNode:RootAction =new RootAction("app.record.create.submit","レコード追加画面","保存するとき");
|
||||
const actionFlow: ActionFlow = new ActionFlow(rootNode);
|
||||
const saibanProps:IActionProperty[]=[{
|
||||
component:"InputText",
|
||||
props:{
|
||||
displayName:"フォーマット",
|
||||
modelValue:"",
|
||||
name:"format",
|
||||
placeholder:"フォーマットを入力してください",
|
||||
}
|
||||
},{
|
||||
component:"FieldInput",
|
||||
props:{
|
||||
displayName:"採番項目",
|
||||
modelValue:"",
|
||||
name:"field",
|
||||
placeholder:"採番項目を選択してください",
|
||||
}
|
||||
}];
|
||||
|
||||
actionFlow.addNode(new ActionNode('自動採番','文書番号を自動採番する','',[],saibanProps));
|
||||
actionFlow.addNode(new ActionNode('入力データ取得','電話番号を取得する',''));
|
||||
const branchNode = actionFlow.addNode(new ActionNode('条件分岐','電話番号入力形式チャック','',['はい','いいえ'] ));
|
||||
// actionFlow.addNode(new ActionNode('入力データ取得','住所を取得する',''),branchNode,'はい');
|
||||
actionFlow.addNode(new ActionNode('エラー表示','エラー表示して保存しない',''),branchNode,'いいえ' );
|
||||
|
||||
// ref関数を使ってtemplateとバインド
|
||||
const state=reactive({
|
||||
activeNode:rootNode,
|
||||
})
|
||||
|
||||
const refFlow = ref(actionFlow);
|
||||
const showAddAction=ref(false);
|
||||
const drawerRight=ref(false);
|
||||
|
||||
const addActionNode=(action:IActionNode)=>{
|
||||
refFlow.value.actionNodes.push(action);
|
||||
}
|
||||
|
||||
const addNode=(node:IActionNode,inputPoint:string)=>{
|
||||
showAddAction.value=true;
|
||||
}
|
||||
|
||||
const onNodeSelected=(node:IActionNode)=>{
|
||||
//右パネルが開いている場合、自動閉じる
|
||||
if(drawerRight.value && state.activeNode.id!==node.id){
|
||||
drawerRight.value=false;
|
||||
}
|
||||
state.activeNode = node;
|
||||
}
|
||||
|
||||
const onNodeEdit=(node:IActionNode)=>{
|
||||
state.activeNode = node;
|
||||
drawerRight.value=true;
|
||||
}
|
||||
|
||||
const onDeleteNode=(node:IActionNode)=>{
|
||||
refFlow.value.removeNode(node);
|
||||
}
|
||||
|
||||
const onDeleteAllNextNodes=(node:IActionNode)=>{
|
||||
refFlow.value.removeAllNext(node.id);
|
||||
}
|
||||
const closeDg=(val :any)=>{
|
||||
console.log("Dialog closed->",val);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flowchart{
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
122
frontend/src/pages/FlowEditorPage.vue
Normal file
122
frontend/src/pages/FlowEditorPage.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="q-ma-md">
|
||||
<div class="q-gutter-xs row items-start">
|
||||
<q-breadcrumbs class="q-pt-xs q-mr-sm" active-color="black">
|
||||
<q-breadcrumbs-el icon="home" />
|
||||
<q-breadcrumbs-el :label="actName" />
|
||||
<q-breadcrumbs-el
|
||||
v-for="flowName in flowNames"
|
||||
:key="flowName"
|
||||
:label="flowName"
|
||||
/>
|
||||
|
||||
<q-breadcrumbs-el :label="flowNames1" />
|
||||
</q-breadcrumbs>
|
||||
<q-separator vertical class="q-mr-xs" />
|
||||
<q-btn
|
||||
unelevated
|
||||
class="q-py-sm"
|
||||
padding="none md none sm"
|
||||
color="blue-1"
|
||||
text-color="primary"
|
||||
size="md"
|
||||
@click="drawerLeft = !drawerLeft"
|
||||
label="変 更"
|
||||
icon="expand_more"
|
||||
dense
|
||||
/>
|
||||
|
||||
<q-space />
|
||||
<q-btn
|
||||
class="q-px-sm q-mr-sm"
|
||||
color="white"
|
||||
size="sm"
|
||||
text-color="black"
|
||||
label="キャンセル"
|
||||
dense
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
class="q-px-sm"
|
||||
color="primary"
|
||||
size="sm"
|
||||
label="保存する"
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-layout
|
||||
container
|
||||
style="height: 91.5dvb"
|
||||
class="shadow-2 rounded-borders"
|
||||
>
|
||||
<q-drawer side="left" overlay bordered v-model="drawerLeft">
|
||||
<div class="q-pa-sm fixed-right">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="primary"
|
||||
icon="close"
|
||||
@click="drawerLeft = !drawerLeft"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="q-mt-lg q-pa-sm">
|
||||
<q-card-section>
|
||||
<div class="flex-center">
|
||||
<div class="row q-pl-md">
|
||||
<p class="text-h6">アクション選択</p>
|
||||
</div>
|
||||
<ItemSelector :actName="actName" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
</div>
|
||||
<q-separator />
|
||||
<div class="q-mt-md q-pa-sm">
|
||||
<q-card-section>
|
||||
<p class="text-h6 q-pl-md q-mb-none">フロー選択</p>
|
||||
<ControlPanel />
|
||||
</q-card-section>
|
||||
</div>
|
||||
<q-separator />
|
||||
<q-card-actions align="right">
|
||||
<div class="q-pa-sm">
|
||||
<q-btn
|
||||
flat
|
||||
color="primary"
|
||||
size="md"
|
||||
@click="drawerLeft = !drawerLeft"
|
||||
label="ジャンプ"
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
</q-card-actions>
|
||||
</q-drawer>
|
||||
|
||||
<FlowChartTest />
|
||||
</q-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FlowChartTest from 'pages/FlowChartTest.vue';
|
||||
import ControlPanel from 'components/flowEditor/left/ControlPanelC.vue';
|
||||
import ItemSelector from 'components/flowEditor/left/ItemSelector.vue';
|
||||
import { ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
|
||||
const actName = ref('勤怠管理 - 4');
|
||||
const flowNames = ref(['レコードを追加画面', '保存をクリックした時']);
|
||||
|
||||
const drawerLeft = ref(false);
|
||||
const store = useFlowEditorStore();
|
||||
const { flowNames1 } = storeToRefs(store);
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="sass"></style>
|
||||
112
frontend/src/pages/LoginPage.vue
Normal file
112
frontend/src/pages/LoginPage.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<q-layout view="lHh Lpr fff">
|
||||
<q-page-container>
|
||||
<q-page class="window-height window-width row justify-center items-center">
|
||||
<div class="column q-pa-lg">
|
||||
<div class="row">
|
||||
<q-card :square="false" class="shadow-24" style="width:400px;height:540px;">
|
||||
<q-card-section class="bg-primary">
|
||||
<h4 class="text-h5 text-white q-my-md">{{ title}}</h4>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-form class="q-px-sm q-pt-xl" ref="loginForm">
|
||||
<q-input square clearable v-model="email" type="email" lazy-rules
|
||||
:rules="[required,isEmail,short]" label="メール">
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="email" />
|
||||
</template>
|
||||
</q-input>
|
||||
<q-input square clearable v-model="password" :type="passwordFieldType" lazy-rules
|
||||
:rules="[required, short]" label="パスワード">
|
||||
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="lock" />
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-icon :name="visibilityIcon" @click="switchVisibility" class="cursor-pointer" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions class="q-px-lg">
|
||||
<q-btn :loading="loading" unelevated size="lg" color="secondary" @click="submit" class="full-width text-white"
|
||||
label="ログイン" >
|
||||
<template v-slot:loading>
|
||||
<q-spinner class="on-left" />
|
||||
ログイン中...
|
||||
</template>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</q-layout>>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useQuasar } from 'quasar'
|
||||
// import { useRouter } from 'vue-router';
|
||||
import { ref } from 'vue';
|
||||
// import { Auth } from '../control/auth'
|
||||
import { useAuthStore } from 'stores/useAuthStore';
|
||||
const authStore = useAuthStore();
|
||||
const $q = useQuasar()
|
||||
const loginForm = ref(null);
|
||||
const loading = ref(false);
|
||||
let title = ref('ログイン');
|
||||
let email = ref('');
|
||||
let password = ref('');
|
||||
let visibility = ref(false);
|
||||
let passwordFieldType = ref('password');
|
||||
let visibilityIcon = ref('visibility');
|
||||
const required = (val:string) => {
|
||||
return (val && val.length > 0 || '必須項目')
|
||||
}
|
||||
const isEmail = (val:string) => {
|
||||
const emailPattern = /^(?=[a-zA-Z0-9@._%+-]{6,254}$)[a-zA-Z0-9._%+-]{1,64}@(?:[a-zA-Z0-9-]{1,63}\.){1,8}[a-zA-Z]{2,63}$/
|
||||
return (emailPattern.test(val) || '無効なメールアドレス')
|
||||
}
|
||||
const short = (val:string) => {
|
||||
return (val && val.length > 3 || '値が短く過ぎる')
|
||||
}
|
||||
const switchVisibility = () => {
|
||||
visibility.value = !visibility.value
|
||||
passwordFieldType.value = visibility.value ? 'text' : 'password'
|
||||
visibilityIcon.value = visibility.value ? 'visibility_off' : 'visibility'
|
||||
}
|
||||
const submit = async () =>{
|
||||
loading.value=true;
|
||||
try {
|
||||
const result = await authStore.login(email.value,password.value);
|
||||
loading.value=false;
|
||||
if(result){
|
||||
$q.notify({
|
||||
icon: 'done',
|
||||
color: 'positive',
|
||||
message: 'ログイン成功'
|
||||
});
|
||||
}
|
||||
else{
|
||||
$q.notify({
|
||||
icon: 'error',
|
||||
color: 'negative',
|
||||
message: 'ログイン失敗'
|
||||
});
|
||||
}
|
||||
}catch (error) {
|
||||
console.error(error);
|
||||
loading.value=false;
|
||||
$q.notify({
|
||||
icon: 'error',
|
||||
color: 'negative',
|
||||
message: 'ログイン失敗'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="q-pa-md">
|
||||
<q-select v-model="model" :options="options" label="Standard"/>
|
||||
<q-btn :label="model+'選択'" color="primary" @click="showDg()" />
|
||||
<show-dialog v-model:visible="show" :name="model" @close="closeDg">
|
||||
<show-dialog v-model:visible="show" :name="model" @close="closeDg" width="400px">
|
||||
<template v-if="model=='アプリ'">
|
||||
<app-select ref="appDg" :name="model" type="single"></app-select>
|
||||
</template>
|
||||
|
||||
206
frontend/src/pages/TenantDomain.vue
Normal file
206
frontend/src/pages/TenantDomain.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table title="Treats" :rows="rows" :columns="columns" row-key="id" selection="single" :filter="filter"
|
||||
:loading="loading" v-model:selected="selected">
|
||||
|
||||
<template v-slot:top>
|
||||
<q-btn color="primary" :disable="loading" label="新規" @click="addRow" />
|
||||
<q-btn class="q-ml-sm" color="primary" :disable="loading" label="編集" @click="editRow" />
|
||||
<q-btn class="q-ml-sm" color="primary" :disable="loading" label="削除" @click="removeRow" />
|
||||
<q-space />
|
||||
<q-input borderless dense debounce="300" color="primary" v-model="filter">
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
</q-table>
|
||||
|
||||
<q-dialog :model-value="show" persistent>
|
||||
<q-card style="min-width: 400px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">Kintone Account</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<q-form class="q-gutter-md">
|
||||
<q-input filled v-model="tenantid" label="Tenant" hint="Tenant ID" lazy-rules
|
||||
:rules="[val => val && val.length > 0 || 'Please type something']" />
|
||||
|
||||
<q-input filled v-model="name" label="Your name *" hint="Kintone envirment name" lazy-rules
|
||||
:rules="[val => val && val.length > 0 || 'Please type something']" />
|
||||
|
||||
<q-input filled type="url" v-model="url" label="Kintone url" hint="Kintone domain address" lazy-rules
|
||||
:rules="[val => val && val.length > 0, isDomain || 'Please type something']" />
|
||||
|
||||
<q-input filled v-model="kintoneuser" label="Login user " hint="Kintone user name" lazy-rules
|
||||
:rules="[val => val && val.length > 0 || 'Please type something']" />
|
||||
|
||||
<q-input v-model="kintonepwd" filled :type="isPwd ? 'password' : 'text'" hint="Password with toggle"
|
||||
label="User password">
|
||||
<template v-slot:append>
|
||||
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer" @click="isPwd = !isPwd" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right" class="text-primary">
|
||||
<q-btn label="Save" type="submit" color="primary" @click="onSubmit" />
|
||||
<q-btn label="Cancel" type="cancel" color="primary" flat class="q-ml-sm" @click="closeDg()" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="confirm" persistent>
|
||||
<q-card>
|
||||
<q-card-section class="row items-center">
|
||||
<q-avatar icon="confirm" color="primary" text-color="white" />
|
||||
<span class="q-ml-sm">削除してもよろしいですか?</span>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn flat label="Cancel" color="primary" v-close-popup />
|
||||
<q-btn flat label="OK" color="primary" v-close-popup @click="deleteDomain()" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue';
|
||||
import { api } from 'boot/axios';
|
||||
|
||||
const columns = [
|
||||
{ name: 'id' },
|
||||
{
|
||||
name: 'tenantid',
|
||||
required: true,
|
||||
label: 'Tenant',
|
||||
align: 'left',
|
||||
field: row => row.tenantid,
|
||||
format: val => `${val}`,
|
||||
sortable: true
|
||||
},
|
||||
{ name: 'name', align: 'center', label: 'Name', field: 'name', sortable: true },
|
||||
{ name: 'url', align: 'left', label: 'URL', field: 'url', sortable: true },
|
||||
{ name: 'user', label: 'Account', field: 'user' },
|
||||
{ name: 'password', label: 'Password', field: 'password' }
|
||||
];
|
||||
|
||||
|
||||
const loading = ref(false);
|
||||
const filter = ref('');
|
||||
const rows = ref([]);
|
||||
const show = ref(false);
|
||||
const confirm = ref(false);
|
||||
const selected = ref([]);
|
||||
const tenantid = ref('');
|
||||
const name = ref('');
|
||||
const url = ref('');
|
||||
const isPwd = ref(true);
|
||||
const kintoneuser = ref('');
|
||||
const kintonepwd = ref('');
|
||||
|
||||
let editId = ref(0);
|
||||
|
||||
const getDomain = async () => {
|
||||
loading.value = true;
|
||||
const result= await api.get(`api/domains/1`);
|
||||
rows.value= result.data.map((item)=>{
|
||||
return { id: item.id, tenantid: item.tenantid, name: item.name, url: item.url, user: item.kintoneuser, password: item.kintonepwd }
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getDomain();
|
||||
})
|
||||
|
||||
// emulate fetching data from server
|
||||
const addRow = () => {
|
||||
editId.value
|
||||
show.value = true;
|
||||
}
|
||||
|
||||
const removeRow = () => {
|
||||
//loading.value = true
|
||||
confirm.value = true;
|
||||
let row = JSON.parse(JSON.stringify(selected.value[0]));
|
||||
if (selected.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
editId.value = row.id;
|
||||
}
|
||||
|
||||
const deleteDomain = () => {
|
||||
api.delete(`api/domain/${editId.value}`).then(() => {
|
||||
getDomain();
|
||||
})
|
||||
editId.value = 0;
|
||||
selected.value = [];
|
||||
};
|
||||
|
||||
const editRow = () => {
|
||||
if (selected.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
let row = JSON.parse(JSON.stringify(selected.value[0]));
|
||||
editId.value = row.id;
|
||||
tenantid.value = row.tenantid;
|
||||
name.value = row.name;
|
||||
url.value = row.url;
|
||||
kintoneuser.value = row.user;
|
||||
kintonepwd.value = row.password;
|
||||
isPwd.value = true;
|
||||
show.value = true;
|
||||
};
|
||||
const closeDg = () => {
|
||||
show.value = false;
|
||||
onReset();
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
if (editId.value !== 0) {
|
||||
api.put(`api/domain`, {
|
||||
'id': editId.value,
|
||||
'tenantid': tenantid.value,
|
||||
'name': name.value,
|
||||
'url': url.value,
|
||||
'kintoneuser': kintoneuser.value,
|
||||
'kintonepwd': kintonepwd.value
|
||||
}).then(() => {
|
||||
getDomain();
|
||||
closeDg();
|
||||
onReset();
|
||||
})
|
||||
}
|
||||
else {
|
||||
api.post(`api/domain`, {
|
||||
'id': 0,
|
||||
'tenantid': tenantid.value,
|
||||
'name': name.value,
|
||||
'url': url.value,
|
||||
'kintoneuser': kintoneuser.value,
|
||||
'kintonepwd': kintonepwd.value
|
||||
}).then(() => {
|
||||
getDomain();
|
||||
closeDg();
|
||||
onReset();
|
||||
})
|
||||
}
|
||||
selected.value = [];
|
||||
}
|
||||
|
||||
const onReset = () => {
|
||||
name.value = '';
|
||||
url.value = '';
|
||||
kintoneuser.value = '';
|
||||
kintonepwd.value = '';
|
||||
isPwd.value = true;
|
||||
editId.value = 0;
|
||||
}
|
||||
</script>
|
||||
270
frontend/src/pages/UserDomain.vue
Normal file
270
frontend/src/pages/UserDomain.vue
Normal file
@@ -0,0 +1,270 @@
|
||||
<!-- <template>
|
||||
<div class="q-pa-md" style="max-width: 400px">
|
||||
|
||||
<q-form
|
||||
@submit="onSubmit"
|
||||
@reset="onReset"
|
||||
class="q-gutter-md"
|
||||
>
|
||||
<q-input
|
||||
filled
|
||||
v-model="name"
|
||||
label="Your name *"
|
||||
hint="Kintone envirment name"
|
||||
lazy-rules
|
||||
:rules="[ val => val && val.length > 0 || 'Please type something']"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
filled type="url"
|
||||
v-model="url"
|
||||
label="Kintone url"
|
||||
hint="Kintone domain address"
|
||||
lazy-rules
|
||||
:rules="[ val => val && val.length > 0,isDomain || 'Please type something']"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
filled
|
||||
v-model="username"
|
||||
label="Login user "
|
||||
hint="Kintone user name"
|
||||
lazy-rules
|
||||
:rules="[ val => val && val.length > 0 || 'Please type something']"
|
||||
/>
|
||||
|
||||
<q-input v-model="password" filled :type="isPwd ? 'password' : 'text'" hint="Password with toggle" label="User password">
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
:name="isPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="isPwd = !isPwd"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-toggle v-model="accept" label="Active Domain" />
|
||||
|
||||
<div>
|
||||
<q-btn label="Submit" type="submit" color="primary"/>
|
||||
<q-btn label="Reset" type="reset" color="primary" flat class="q-ml-sm" />
|
||||
</div>
|
||||
</q-form>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useQuasar } from 'quasar'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
setup () {
|
||||
const $q = useQuasar()
|
||||
|
||||
const name = ref(null)
|
||||
const age = ref(null)
|
||||
const accept = ref(false)
|
||||
const isPwd =ref(true)
|
||||
|
||||
return {
|
||||
name,
|
||||
age,
|
||||
accept,
|
||||
isPwd,
|
||||
isDomain(val) {
|
||||
const domainPattern = /^https?\/\/:([a-zA-Z] +\.){1}([a-zA-Z]+)\.([a-zA-Z]+)$/;
|
||||
return (domainPattern.test(val) || '無効なURL')
|
||||
},
|
||||
|
||||
onSubmit () {
|
||||
if (accept.value !== true) {
|
||||
$q.notify({
|
||||
color: 'red-5',
|
||||
textColor: 'white',
|
||||
icon: 'warning',
|
||||
message: 'You need to accept the license and terms first'
|
||||
})
|
||||
}
|
||||
else {
|
||||
$q.notify({
|
||||
color: 'green-4',
|
||||
textColor: 'white',
|
||||
icon: 'cloud_done',
|
||||
message: 'Submitted'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
onReset () {
|
||||
name.value = null
|
||||
age.value = null
|
||||
accept.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script> -->
|
||||
|
||||
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table grid grid-header title="Domain" selection="single" :rows="rows" :columns="columns" v-model:selected="selected" row-key="name" :filter="filter" hide-header>
|
||||
<template v-slot:top>
|
||||
<div class="q-pa-md q-gutter-sm">
|
||||
<q-btn color="primary" label="追加" @click="newDomain()" dense />
|
||||
</div>
|
||||
<q-space />
|
||||
<q-input borderless dense debounce="300" v-model="filter" placeholder="Search">
|
||||
<template v-slot:append>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
<template v-slot:item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="q-table__grid-item-row">
|
||||
<div class="q-table__grid-item-title">Domain</div>
|
||||
<div class="q-table__grid-item-value">{{ props.row.name }}</div>
|
||||
</div>
|
||||
<div class="q-table__grid-item-row">
|
||||
<div class="q-table__grid-item-title">URL</div>
|
||||
<div class="q-table__grid-item-value">{{ props.row.url }}</div>
|
||||
</div>
|
||||
<div class="q-table__grid-item-row">
|
||||
<div class="q-table__grid-item-title">Account</div>
|
||||
<div class="q-table__grid-item-value">{{ props.row.kintoneuser }}</div>
|
||||
</div>
|
||||
<div class="q-table__grid-item-row">
|
||||
<div class="q-table__grid-item-value">{{isActive(props.row.id) }}</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-separator />
|
||||
<q-card-actions align="right">
|
||||
<q-btn flat @click = "activeDomain(props.row.id)">有効</q-btn>
|
||||
<q-btn flat @click = "deleteConfirm(props.row)">削除</q-btn>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
|
||||
<show-dialog v-model:visible="show" name="ドメイン" @close="closeDg" width="350px">
|
||||
<domain-select ref="domainDg" name="ドメイン" type="multiple"></domain-select>
|
||||
</show-dialog>
|
||||
|
||||
<q-dialog v-model="confirm" persistent>
|
||||
<q-card>
|
||||
<q-card-section class="row items-center">
|
||||
<q-avatar icon="confirm" color="primary" text-color="white" />
|
||||
<span class="q-ml-sm">削除してもよろしいですか?</span>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn flat label="Cancel" color="primary" v-close-popup />
|
||||
<q-btn flat label="OK" color="primary" v-close-popup @click = "deleteDomain()"/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useQuasar } from 'quasar'
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import ShowDialog from 'components/ShowDialog.vue';
|
||||
import DomainSelect from 'components/DomainSelect.vue';
|
||||
import { useAuthStore } from 'stores/useAuthStore';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
import { api } from 'boot/axios';
|
||||
import { domain } from 'process';
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
const domainDg = ref();
|
||||
const selected = ref([])
|
||||
|
||||
const show = ref(false);
|
||||
const confirm = ref(false)
|
||||
|
||||
let editId = ref(0);
|
||||
let activedomainid = ref(0);
|
||||
|
||||
const columns = [
|
||||
{ name: 'id'},
|
||||
{name: 'name',required: true,label: 'Name',align: 'left',field: 'name',sortable: true},
|
||||
{ name: 'url', align: 'center', label: 'Domain', field: 'url', sortable: true },
|
||||
{ name: 'kintoneuser', label: 'User', field: 'kintoneuser', sortable: true },
|
||||
{ name: 'kintonepwd' },
|
||||
{ name: 'active', field: 'active'}
|
||||
]
|
||||
|
||||
const rows = ref([] as any[]);
|
||||
|
||||
const isActive = (id:number) =>{
|
||||
if(id == activedomainid.value)
|
||||
return "Active";
|
||||
else
|
||||
return "Inactive";
|
||||
}
|
||||
|
||||
const newDomain = () => {
|
||||
editId.value = 0;
|
||||
show.value = true;
|
||||
};
|
||||
|
||||
|
||||
const activeDomain = (id:number) => {
|
||||
api.put(`api/activedomain/`+ id).then(() =>{
|
||||
getDomain();
|
||||
})
|
||||
};
|
||||
|
||||
const deleteConfirm = (row:object) => {
|
||||
confirm.value = true;
|
||||
editId.value = row.id;
|
||||
};
|
||||
|
||||
const deleteDomain = () => {
|
||||
api.delete(`api/domain/`+ editId.value+'/1').then(() =>{
|
||||
getDomain();
|
||||
})
|
||||
editId.value = 0;
|
||||
};
|
||||
|
||||
const closeDg = (val:string) => {
|
||||
if (val == 'OK') {
|
||||
let dodmainids =[];
|
||||
let domains = JSON.parse(JSON.stringify(domainDg.value.selected));
|
||||
for(var key in domains)
|
||||
{
|
||||
dodmainids.push(domains[key].id);
|
||||
}
|
||||
api.post(`api/domain`, dodmainids).then(() =>{getDomain();});
|
||||
}
|
||||
|
||||
};
|
||||
const getDomain = async () => {
|
||||
const resp = await api.get(`api/activedomain`);
|
||||
activedomainid.value = resp.data.id;
|
||||
const domainResult = await api.get(`api/domain`);
|
||||
const domains = domainResult.data as any[];
|
||||
rows.value=domains.map((item)=>{
|
||||
return { id:item.id,name: item.name, url: item.url, kintoneuser: item.kintoneuser, kintonepwd: item.kintonepwd}
|
||||
});
|
||||
}
|
||||
onMounted(async () => {
|
||||
await getDomain();
|
||||
})
|
||||
|
||||
const isDomain = (val) =>{
|
||||
// const domainPattern = /^https\/\/:([a-zA-Z] +\.){1}([a-zA-Z]+)\.([a-zA-Z]+)$/;
|
||||
// return (domainPattern.test(val) || '無効なURL')
|
||||
return true;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
39
frontend/src/pages/conditionPage.vue
Normal file
39
frontend/src/pages/conditionPage.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<q-page>
|
||||
<div class="flowchart">
|
||||
<q-btn @click="showCondition()" class="q-mt-md" color="primary" icon="mdi-plus">条件エディタ表示</q-btn>
|
||||
</div>
|
||||
<condition-editor v-model:show="show" v-model:conditionTree="tree"></condition-editor>
|
||||
<q-code>{{conditionString}}</q-code>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref,reactive,computed} from 'vue';
|
||||
import ConditionEditor from '../components/ConditionEditor/ConditionEditor.vue';
|
||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||
import { ConditionTree,GroupNode,ConditionNode,LogicalOperator,Operator } from 'app/src/types/Conditions';
|
||||
|
||||
const store = useFlowEditorStore();
|
||||
const tree = reactive(new ConditionTree());
|
||||
const newNode = new ConditionNode({},Operator.Equal,'',tree.root);
|
||||
tree.addNode(tree.root,newNode);
|
||||
|
||||
const show =ref(false);
|
||||
const showCondition=()=>{
|
||||
show.value=true;
|
||||
}
|
||||
const conditionString = computed(()=>{
|
||||
return tree.buildConditionString(tree.root);
|
||||
});
|
||||
store.setApp({
|
||||
appId:'146',
|
||||
name:'トリトン管理部日報'
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flowchart {
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
96
frontend/src/pages/testFlow.vue
Normal file
96
frontend/src/pages/testFlow.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="q-pa-md q-gutter-sm">
|
||||
<q-btn label="プロパティ" icon="keyboard_arrow_right" color="primary" @click="open('right')" />
|
||||
<!-- <q-btn label="Readプロパティ" icon="keyboard_arrow_right" color="primary" @click="write('right')" /> -->
|
||||
|
||||
<q-dialog v-model="dialog" :position="position">
|
||||
<q-card class="column full-height" style="width: 300px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">プロパティ</div>
|
||||
</q-card-section>
|
||||
|
||||
|
||||
<q-card-section class="col q-pt-none">
|
||||
<ActionProperty :jsonData="jsonData" :jsonValue="jsonValue"/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="bg-white text-teal">
|
||||
<q-btn flat label="Save" v-close-popup @click="save"/>
|
||||
<q-btn flat label="Cancel" v-close-popup />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref,onMounted } from 'vue'
|
||||
import ActionProperty from 'components/right/ActionProperty.vue';
|
||||
const dialog = ref(false)
|
||||
const position = ref('top')
|
||||
|
||||
const jsonData = {
|
||||
elements: [
|
||||
{
|
||||
component: 'InputText',
|
||||
props: {
|
||||
name:'1',
|
||||
placeholder: 'Enter some text',
|
||||
modelValue: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'SelectBox',
|
||||
props: {
|
||||
name:'2',
|
||||
placeholder: 'Choose an option',
|
||||
modelValue: '',
|
||||
options: [
|
||||
'option1',
|
||||
'option2',
|
||||
'option3'
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
props: {
|
||||
name:'3',
|
||||
placeholder: 'Choose a date',
|
||||
modelValue: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'FieldInput',
|
||||
props: {
|
||||
name:'4',
|
||||
placeholder: 'Choose a field',
|
||||
modelValue: '',
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
let jsonValue = {
|
||||
1:'abc',
|
||||
2:'option2',
|
||||
3:'2023/09/04',
|
||||
4:'6666'
|
||||
};
|
||||
|
||||
const open = (pos:string) => {
|
||||
position.value = pos
|
||||
dialog.value = true
|
||||
};
|
||||
|
||||
const save = async () =>{
|
||||
|
||||
jsonData.elements.forEach(property => {
|
||||
if(jsonValue != undefined)
|
||||
{
|
||||
jsonValue[property.props.name] = property.props.modelValue;
|
||||
}
|
||||
});
|
||||
console.log(jsonValue);
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="q-pa-md column content-center items-center">
|
||||
<div>
|
||||
<q-btn label="アクション選択" color="primary" @click="showDg()" v-if="addshow" />
|
||||
<show-dialog v-model:visible="show" name="アクション" @close="closeDg">
|
||||
<show-dialog v-model:visible="show" name="アクション" @close="closeDg" width="350px">
|
||||
<action-select ref="appDg" name="アクション" type="single"></action-select>
|
||||
</show-dialog>
|
||||
</div>
|
||||
|
||||
101
frontend/src/pages/testRight.vue
Normal file
101
frontend/src/pages/testRight.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="q-pa-md q-gutter-sm">
|
||||
<q-btn label="プロパティ" icon="keyboard_arrow_right" color="primary" @click="drawerRight = !drawerRight" />
|
||||
<!-- <q-btn label="Readプロパティ" icon="keyboard_arrow_right" color="primary" @click="write('right')" /> -->
|
||||
<q-drawer
|
||||
side="right"
|
||||
v-model="drawerRight"
|
||||
show-if-above
|
||||
bordered
|
||||
:width="301"
|
||||
:breakpoint="500"
|
||||
class="bg-grey-3"
|
||||
>
|
||||
<q-card class="column full-height" style="width: 300px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">プロパティ</div>
|
||||
</q-card-section>
|
||||
|
||||
|
||||
<q-card-section class="col q-pt-none">
|
||||
<ActionProperty :jsonData="jsonData" :jsonValue="jsonValue" v-if="drawerRight"/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="bg-white text-teal">
|
||||
<q-btn flat label="Save" @click="save"/>
|
||||
<q-btn flat label="Cancel" @click="cancel" />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import ActionProperty from 'components/right/ActionProperty.vue';
|
||||
const drawerRight = ref(false);
|
||||
const jsonData = {
|
||||
elements: [
|
||||
{
|
||||
component: 'InputText',
|
||||
props: {
|
||||
name:'1',
|
||||
placeholder: 'Enter some text',
|
||||
modelValue: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'SelectBox',
|
||||
props: {
|
||||
name:'2',
|
||||
placeholder: 'Choose an option',
|
||||
modelValue: '',
|
||||
options: [
|
||||
'option1',
|
||||
'option2',
|
||||
'option3'
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
props: {
|
||||
name:'3',
|
||||
placeholder: 'Choose a date',
|
||||
modelValue: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'FieldInput',
|
||||
props: {
|
||||
name:'4',
|
||||
placeholder: 'Choose a field',
|
||||
modelValue: '',
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
let jsonValue = {
|
||||
1:'abc',
|
||||
2:'option2',
|
||||
3:'2023/09/04',
|
||||
4:'6666'
|
||||
};
|
||||
|
||||
const cancel = async() =>{
|
||||
drawerRight.value = false;
|
||||
}
|
||||
|
||||
const save = async () =>{
|
||||
|
||||
jsonData.elements.forEach(property => {
|
||||
if(jsonValue != undefined)
|
||||
{
|
||||
jsonValue[property.props.name] = property.props.modelValue;
|
||||
}
|
||||
});
|
||||
console.log(jsonValue);
|
||||
drawerRight.value=false;
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from 'vue-router';
|
||||
|
||||
import routes from './routes';
|
||||
|
||||
import { useAuthStore } from 'stores/useAuthStore';
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Router instantiation;
|
||||
@@ -17,20 +17,60 @@ import routes from './routes';
|
||||
* with the Router instance.
|
||||
*/
|
||||
|
||||
export default route(function (/* { store, ssrContext } */) {
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory);
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory);
|
||||
|
||||
const Router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
routes,
|
||||
const routerInstance = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
routes,
|
||||
|
||||
// Leave this as is and make changes in quasar.conf.js instead!
|
||||
// quasar.conf.js -> build -> vueRouterMode
|
||||
// quasar.conf.js -> build -> publicPath
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||
});
|
||||
|
||||
return Router;
|
||||
// Leave this as is and make changes in quasar.conf.js instead!
|
||||
// quasar.conf.js -> build -> vueRouterMode
|
||||
// quasar.conf.js -> build -> publicPath
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||
});
|
||||
|
||||
export default route(function (/* { store, ssrContext } */) {
|
||||
|
||||
routerInstance.beforeEach(async (to) => {
|
||||
// clear alert on route change
|
||||
//const alertStore = useAlertStore();
|
||||
//alertStore.clear();
|
||||
|
||||
// redirect to login page if not logged in and trying to access a restricted page
|
||||
const publicPages = ['/login'];
|
||||
const authRequired = !publicPages.includes(to.path);
|
||||
const authStore = useAuthStore();
|
||||
|
||||
if (authRequired && !authStore.token) {
|
||||
authStore.returnUrl = to.fullPath;
|
||||
return '/login';
|
||||
}
|
||||
});
|
||||
return routerInstance;
|
||||
});
|
||||
|
||||
export const router = routerInstance;
|
||||
|
||||
|
||||
// const createHistory = process.env.SERVER
|
||||
// ? createMemoryHistory
|
||||
// : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory);
|
||||
|
||||
// export const Router = createRouter({
|
||||
// scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
// routes,
|
||||
|
||||
// // Leave this as is and make changes in quasar.conf.js instead!
|
||||
// // quasar.conf.js -> build -> vueRouterMode
|
||||
// // quasar.conf.js -> build -> publicPath
|
||||
// history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||
// });
|
||||
|
||||
// export default route(function (/* { store, ssrContext } */) {
|
||||
// return Router;
|
||||
// });
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('pages/LoginPage.vue')
|
||||
},
|
||||
{
|
||||
path:'/FlowChart',
|
||||
component:()=>import('layouts/MainLayout.vue'),
|
||||
children:[
|
||||
{path:'',component:()=>import('pages/FlowChart.vue')}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
children: [{ path: '', component: () => import('pages/IndexPage.vue') }],
|
||||
children: [
|
||||
{ path: '', component: () => import('pages/IndexPage.vue') },
|
||||
{ path: 'ruleEditor', component: () => import('pages/RuleEditor.vue') },
|
||||
{ path: 'test', component: () => import('pages/testQursar.vue') },
|
||||
{ path: 'flow', component: () => import('pages/testFlow.vue') },
|
||||
{ path: 'FlowChartTest', component: () => import('pages/FlowChartTest.vue') },
|
||||
{ path: 'flowEditor', component: () => import('pages/FlowEditorPage.vue') },
|
||||
// { path: 'FlowChart', component: () => import('pages/FlowChart.vue') },
|
||||
{ path: 'right', component: () => import('pages/testRight.vue') },
|
||||
{ path: 'domain', component: () => import('pages/TenantDomain.vue') },
|
||||
{ path: 'userdomain', component: () => import('pages/UserDomain.vue')},
|
||||
{ path: 'condition', component: () => import('pages/conditionPage.vue') }
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/ruleEditor/',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
children: [{ path: '', component: () => import('pages/RuleEditor.vue') }],
|
||||
},
|
||||
{
|
||||
path: '/test/',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
children: [{ path: '', component: () => import('pages/testQursar.vue') }],
|
||||
},
|
||||
|
||||
// Always leave this as last one,
|
||||
// but you can also remove it
|
||||
{
|
||||
|
||||
118
frontend/src/stores/flowEditor.ts
Normal file
118
frontend/src/stores/flowEditor.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { AppInfo ,IActionFlow, IActionNode} from 'src/types/ActionTypes';
|
||||
import { IKintoneEvent,KintoneEventManager } from 'src/types/KintoneEvents';
|
||||
import {FlowCtrl } from '../control/flowctrl';
|
||||
|
||||
export interface FlowEditorState{
|
||||
flowNames1:string;
|
||||
appInfo?:AppInfo;
|
||||
flows?:IActionFlow[];
|
||||
selectedFlow?:IActionFlow|undefined;
|
||||
activeNode:IActionNode|undefined;
|
||||
eventTree:KintoneEventManager;
|
||||
selectedEvent:IKintoneEvent|undefined;
|
||||
expandedScreen:any[];
|
||||
}
|
||||
const flowCtrl=new FlowCtrl();
|
||||
const eventTree = new KintoneEventManager();
|
||||
export const useFlowEditorStore = defineStore("flowEditor",{
|
||||
state: ():FlowEditorState => ({
|
||||
flowNames1: '',
|
||||
appInfo:undefined,
|
||||
flows:[],
|
||||
selectedFlow:undefined,
|
||||
activeNode:undefined,
|
||||
eventTree:eventTree,
|
||||
selectedEvent:undefined,
|
||||
expandedScreen:[]
|
||||
}),
|
||||
getters: {
|
||||
/**
|
||||
*
|
||||
* @returns 現在編集しているフロー
|
||||
*/
|
||||
currentFlow():IActionFlow|undefined{
|
||||
return this.selectedFlow;
|
||||
},
|
||||
/**
|
||||
* KintoneイベントIDから、バンドしているフローを検索する
|
||||
* @param state
|
||||
* @returns
|
||||
*/
|
||||
findFlowByEventId(state){
|
||||
return (eventId:string)=>{
|
||||
return state.flows?.find((flow)=>{
|
||||
const root=flow.getRoot();
|
||||
return root?.name===eventId
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
setFlows(flows:IActionFlow[]){
|
||||
this.flows=flows;
|
||||
},
|
||||
selectFlow(flow:IActionFlow){
|
||||
this.selectedFlow=flow;
|
||||
},
|
||||
setActiveNode(node:IActionNode){
|
||||
this.activeNode=node;
|
||||
},
|
||||
setApp(app:AppInfo){
|
||||
this.appInfo=app;
|
||||
},
|
||||
/**
|
||||
* DBからフルーを保存する
|
||||
* @returns
|
||||
*/
|
||||
async loadFlow(){
|
||||
if(this.appInfo===undefined) return;
|
||||
const actionFlows = await flowCtrl.getFlows(this.appInfo?.appId);
|
||||
//eventTreeにバンドする
|
||||
this.eventTree.bindFlows(actionFlows);
|
||||
if(actionFlows===undefined || actionFlows.length===0){
|
||||
this.flows=[];
|
||||
this.selectedFlow=undefined;
|
||||
return;
|
||||
}
|
||||
this.setFlows(actionFlows);
|
||||
if(actionFlows && actionFlows.length>0){
|
||||
this.selectFlow(actionFlows[0]);
|
||||
}
|
||||
const expandNames = actionFlows.map(flow=>flow.getRoot()?.title);
|
||||
// const expandName =actionFlows[0].getRoot()?.title;
|
||||
this.expandedScreen=expandNames;
|
||||
},
|
||||
/**
|
||||
* フローをDBに保存及び更新する
|
||||
*/
|
||||
async saveFlow(flow:IActionFlow){
|
||||
const root=flow.getRoot();
|
||||
const isNew = flow.id==='';
|
||||
const jsonData={
|
||||
flowid: isNew ? flow.createNewId():flow.id,
|
||||
appid: this.appInfo?.appId,
|
||||
eventid: root?.name,
|
||||
name: root?.subTitle,
|
||||
content: JSON.stringify(flow)
|
||||
}
|
||||
|
||||
if(isNew){
|
||||
return await flowCtrl.SaveFlow(jsonData);
|
||||
}else{
|
||||
return await flowCtrl.UpdateFlow(jsonData);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* デプロイする
|
||||
*/
|
||||
async deploy():Promise<boolean>{
|
||||
if(this.appInfo===undefined){
|
||||
return false;
|
||||
}
|
||||
return await flowCtrl.depoly(this.appInfo?.appId);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
32
frontend/src/stores/index.ts
Normal file
32
frontend/src/stores/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { store } from 'quasar/wrappers'
|
||||
import { createPinia } from 'pinia'
|
||||
import { Router } from 'vue-router';
|
||||
|
||||
/*
|
||||
* When adding new properties to stores, you should also
|
||||
* extend the `PiniaCustomProperties` interface.
|
||||
* @see https://pinia.vuejs.org/core-concepts/plugins.html#typing-new-store-properties
|
||||
*/
|
||||
declare module 'pinia' {
|
||||
export interface PiniaCustomProperties {
|
||||
readonly router: Router;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Store instantiation;
|
||||
*
|
||||
* The function below can be async too; either use
|
||||
* async/await or return a Promise which resolves
|
||||
* with the Store instance.
|
||||
*/
|
||||
|
||||
export default store((/* { ssrContext } */) => {
|
||||
const pinia = createPinia()
|
||||
|
||||
// You can add Pinia plugins here
|
||||
// pinia.use(SomePiniaPlugin)
|
||||
|
||||
return pinia
|
||||
})
|
||||
10
frontend/src/stores/store-flag.d.ts
vendored
Normal file
10
frontend/src/stores/store-flag.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||
import "quasar/dist/types/feature-flag";
|
||||
|
||||
declare module "quasar/dist/types/feature-flag" {
|
||||
interface QuasarFeatureFlags {
|
||||
store: true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user