Compare commits

...

105 Commits

Author SHA1 Message Date
Mouriya
c3b560dbc9 条件付きコンポーネントは'source'でappidを受け取ることができる。 2024-05-25 04:15:09 +09:00
53aadfcaaa feat:データ集計処理作成 2024-05-24 09:20:19 +09:00
Mouriya
7fb3d08ccb 細部の問題の修正 2024-05-20 03:38:27 +09:00
Mouriya
cf4209333d データ処理書き込み完了 2024-05-17 23:32:14 +09:00
Mouriya
61ac281134 verNameのラッピング・オブジェクト 2024-05-17 14:41:15 +09:00
Mouriya
64d2cadd82 2つのデータ計算コンポーネントを追加する 2024-05-13 06:56:44 +09:00
Mouriya
371e2ee073 add vueuse dependencies 2024-05-13 06:56:03 +09:00
c426bbf793 feat: numInput属性UI追加
inputText,numInputにrules設定追加、入力ルール設定可能
2024-05-09 10:49:05 +09:00
329debaab8 Merge branch 'mvp_step2_dev' into feature-colorpicker 2024-05-07 11:56:20 +09:00
Mouriya
994a0174f5 カラーピッカーと数字入力ボックスの追加 2024-05-06 20:58:06 +09:00
2846297112 アプリからフィールド選択の複数選択・単一選択対応 2024-04-30 12:54:20 +09:00
5cf60ddfdc Merge branch 'mvp_step2_dev' into feature-appfieldselect 2024-04-30 12:09:38 +09:00
0de33f04bc アプリからフィールド選択UI追加 2024-04-25 09:46:34 +09:00
472353632c 長い説明文追加 2024-04-22 22:45:03 +09:00
Mouriya
1a48fb5b20 update 2024-04-22 22:05:39 +09:00
99d3a01991 条件式のバッグ修正 2024-04-18 18:55:59 +09:00
ecb90e7120 ダイアログに検索追加 2024-04-15 16:54:33 +09:00
5349c46225 不要ファイル削除 2024-03-29 10:52:29 +09:00
3be4402239 アクションフローBugFix 2024-03-27 02:01:12 +09:00
4c482ea289 条件アクションの障害修正 2024-03-26 17:08:26 +09:00
44a73478a7 フローエディタの左パネル表示・非表示機能追加 2024-03-12 18:04:50 +09:00
bceac2f172 config変更 2024-03-04 12:53:23 +09:00
98842db343 Merge branch 'mvp_step2_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_step2_dev 2024-03-01 22:49:41 +09:00
03904a4e35 ボタンクリックイベント対応 2024-03-01 22:47:37 +09:00
09b3c8df47 eventaction blacklist 2024-02-28 18:53:40 +09:00
26761f6d39 add eventgroup->event 2024-02-28 18:32:57 +09:00
72608a8ffd アクションフローの不具合改修 2024-02-26 12:20:31 +09:00
d1ec123c8b ActionFlow障害修正 2024-02-25 02:52:06 +09:00
4102ff5522 変数設定を追加 2024-02-21 16:28:43 +09:00
08e857884b Merge branch 'mvp_step2_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_step2_dev 2024-02-17 23:35:20 +09:00
c0db2d230b 条件式比較バグ修正 2024-02-16 14:09:43 +09:00
2b9b772b39 設計書取込機能追加 2024-02-13 14:34:53 +09:00
c46e8a7047 Merge branch 'mvp_step2_back' into mvp_step2_dev 2024-02-13 14:25:49 +09:00
6e6350d6ce 設計書取込機能追加 2024-02-13 14:25:09 +09:00
1e7d553bd6 kintone excel new format 2024-02-06 17:37:55 +09:00
35ae2539cb kintone excel new format 2024-02-06 17:37:29 +09:00
wtl
a614d754f4 ErrorShowNewVer 2024-02-06 15:33:47 +09:00
46a6ba534e Merge branch 'mvp_step2_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_step2_dev 2024-02-03 11:56:13 +09:00
8fecde4c42 left menu 修正 2024-02-03 11:55:44 +09:00
wtl
3e73799532 New Plagin Error Show 2024-02-02 12:52:00 +09:00
3159366560 Actionアドインの開発手順作成 2024-02-01 10:58:39 +09:00
5176cff2bd action開発手順作成 2024-01-31 15:34:29 +09:00
978aa723ae action開発手順作成 2024-01-31 15:33:18 +09:00
926c338f73 Merge branch 'mvp_step2_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_step2_dev 2024-01-31 05:37:04 +09:00
6de60c82ba 条件エディタ実装 2024-01-31 05:22:09 +09:00
6ed17a50e5 token 15m->48h 2024-01-30 12:36:33 +09:00
5cd6d02f6e 条件エディタ追加 2024-01-22 10:52:55 +09:00
276e5e9122 condition test 2023-12-25 17:11:11 +09:00
6e75a2a524 condtion tree 2023-12-25 17:07:40 +09:00
ea6e603036 update APIException 2023-11-22 14:35:37 +09:00
edad30e264 exception の変更 2023-11-21 21:50:50 +09:00
58616100f4 Merge branch 'mvp_step2_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_step2_dev 2023-11-21 21:22:47 +09:00
359558bad3 add dev build mode 2023-11-21 21:22:30 +09:00
6a6554ed1f add APIException 2023-11-21 21:20:02 +09:00
e20625abdb domain切替バグ修正 2023-11-20 01:50:02 +09:00
f83dd693d5 log bugfix 2023-11-19 13:37:55 +09:00
a464297511 add api.log&errorlog->db 2023-11-19 13:24:29 +09:00
991c8e8083 add api.log&errorlog->db 2023-11-19 13:24:08 +09:00
9ea183ff2d 処理中表示追加 2023-11-15 03:13:24 +09:00
34d368b730 ダイアログ表示時snipper追加 2023-11-15 00:18:06 +09:00
17760a6926 FlowChart のレイアウト修正 2023-11-14 09:11:46 +09:00
8b9cfa34c7 Merge branch 'mvp_step2_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_step2_dev 2023-11-13 22:04:27 +09:00
5fb8fe53bb backend bug fix 2023-11-13 22:03:45 +09:00
5cb9375db3 add domainid->flow 2023-11-13 18:02:19 +09:00
55181f2c57 ユーザー追加API Bug fix 2023-11-10 02:34:50 +09:00
4577df371a Token無効の際再ログイン対応 2023-11-09 13:47:21 +09:00
0f154832a5 前端APIのURL参数化対応およびバックエンドのBugFix 2023-11-08 15:44:42 +09:00
5951fcc108 depoly bug fix 2023-11-04 22:54:21 +09:00
7966217ac2 add js dev model 2023-11-04 22:40:40 +09:00
64851bd51d added deploy DEV mode 2023-11-04 22:35:23 +09:00
10e584d2ac bugfix 2023-11-04 22:22:26 +09:00
b97a728624 kintone ENV bugfix 2023-11-04 22:16:23 +09:00
5a875e6853 domain bug fix 2023-11-04 22:11:32 +09:00
57a4823f61 Merge branch 'mvp_feature3_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_feature3_dev 2023-11-04 17:17:08 +09:00
761eb4c13e API token 2023-11-04 17:16:57 +09:00
354fc6868d remove id from localStorage 2023-11-04 17:15:17 +09:00
2538e4526f first&last name 2023-11-04 17:13:44 +09:00
26890f5b35 Merge branch 'mvp_feature3_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_feature3_dev 2023-11-04 15:15:44 +09:00
086b5e2621 Azure env 2023-11-04 15:15:35 +09:00
617b060869 login user 2023-11-04 15:12:27 +09:00
a782e92bd6 自動採番障害対応 2023-11-01 22:59:23 +09:00
f60f97380f domain 2023-11-01 22:56:47 +09:00
cfc416fd14 自動採番アクション追加・ドメイン追加 2023-11-01 10:47:24 +09:00
df593d2ffe modified ER 2023-10-31 03:42:25 +09:00
9cd4c8a5ab event&eventaction 2023-10-29 17:16:50 +09:00
maxiaozhe
ead6658455 floweditor 修正 2023-10-26 09:15:34 +09:00
f6d677b51f Kintone plugin 実装 2023-10-24 09:08:45 +09:00
25f05ab018 Kintoneがわファイルアップロード 2023-10-20 12:28:07 +09:00
178cf33949 deploy機能実装 2023-10-16 17:13:14 +09:00
b54c0f8022 Merge branch 'mvp_step2_dev' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into mvp_step2_dev 2023-10-16 14:02:16 +09:00
c5cc3c1a24 UI修正 2023-10-16 14:02:01 +09:00
0b414fbfbe フロー保存の実装 2023-10-16 13:38:51 +09:00
981d7a5062 bugfix 2023-10-15 23:35:06 +09:00
33fc0b74ef createjstokintone add 2023-10-15 23:34:12 +09:00
286acc4584 upload kintone jscss file 2023-10-15 19:30:35 +09:00
cdfb1d4310 Merge branch 'maxz-real-impl' into mvp_step2_dev 2023-10-15 18:17:32 +09:00
6844652b5d ER図変更 2023-10-12 16:29:51 +09:00
76457b6667 UserDomain Add 2023-10-09 15:55:58 +09:00
e1f2afa942 sqlserver->postgresql 2023-09-30 15:16:05 +09:00
8d5dff60f1 Merge branch 'maxz-real-impl' into mvp_step2_dev 2023-09-30 13:33:39 +09:00
6aa057e590 flow api add 2023-09-21 07:22:12 +00:00
2721cd60d1 action bugfix 2023-09-18 02:54:53 +00:00
1f8d079d4d action bugfix 2023-09-18 02:54:35 +00:00
3ae685a0e2 action add subtitle 2023-09-17 08:54:54 +00:00
c1e50736e8 action table add 2023-09-16 01:52:15 +00:00
109 changed files with 11161 additions and 3014 deletions

2
backend/.gitignore vendored
View File

@@ -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/

View File

@@ -1,5 +1,5 @@
FROM python:3.8
FROM python:3.11.4
RUN mkdir /app
WORKDIR /app

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -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}

View File

@@ -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:
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")
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 = 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,64 +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():
@r.post("/updatejscss")
async def jscss(request:Request,app:str,files:t.List[UploadFile] = File(...),env = Depends(getkintoneenv)):
try:
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,timeout=httpx.Timeout(10))
return r.json()
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:
print(f"異常発生しました。{type(e).__name__},{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"
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"
@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"}
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}{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))
result = r.json()
if result.get("app") != None:
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/deploy.json"
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:
@@ -318,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"}
@@ -419,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)

View File

@@ -4,6 +4,8 @@ from app.db.session import get_db
from app.db.crud import *
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()
@@ -17,9 +19,11 @@ async def appsetting_details(
id: int,
db=Depends(get_db),
):
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(
@@ -27,7 +31,10 @@ async def appsetting_create(
app: AppBase,
db=Depends(get_db),
):
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(
@@ -39,7 +46,10 @@ async def appsetting_edit(
app: AppEdit,
db=Depends(get_db),
):
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(
@@ -50,8 +60,10 @@ async def appsettings_delete(
id: int,
db=Depends(get_db),
):
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(
@@ -64,8 +76,11 @@ async def kintone_data(
type: int,
db=Depends(get_db),
):
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",
@@ -77,8 +92,11 @@ 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}",
@@ -90,8 +108,11 @@ async def flow_details(
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(
@@ -102,19 +123,30 @@ async def flow_details(
async def flow_list(
request: Request,
appid: str,
user=Depends(get_current_user),
db=Depends(get_db),
):
flows = get_flows_by_app(db, appid)
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),
):
return create_flow(db, flow)
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(
@@ -125,7 +157,10 @@ async def flow_edit(
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(
@@ -136,5 +171,175 @@ async def flow_delete(
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)

View 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()

View File

@@ -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,
),

View File

@@ -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://e84c-133-139-70-142.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"))

View File

@@ -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

View File

@@ -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
@@ -124,11 +125,12 @@ def get_actions(db: Session):
return actions
def create_flow(db: Session, flow: schemas.FlowBase):
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
)
@@ -175,8 +177,122 @@ def get_flow(db: Session, flowid: str):
raise HTTPException(status_code=404, detail="Data not found")
return flow
def get_flows_by_app(db: Session, appid: str):
flows = db.query(models.Flow).filter(models.Flow.appid == appid).all()
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 HTTPException(status_code=404, detail="Data not found")
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

View File

@@ -1,4 +1,4 @@
from sqlalchemy import Boolean, Column, Integer, String, DateTime
from sqlalchemy import Boolean, Column, Integer, String, DateTime,ForeignKey
from sqlalchemy.ext.declarative import as_declarative
from datetime import datetime
@@ -47,5 +47,67 @@ class Flow(Base):
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))

View File

@@ -20,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
@@ -46,6 +51,7 @@ class Token(BaseModel):
class TokenData(BaseModel):
id:int = 0
email: str = None
permissions: str = "user"
@@ -98,8 +104,45 @@ class Flow(Base):
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

View File

@@ -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():

View File

@@ -21,3 +21,4 @@ pandas==2.0.3
openpyxl==3.1.2
deepdiff==6.3.1
pymssql==2.2.7
psycopg2==2.9.8

View File

@@ -1,6 +1,6 @@
<mxfile host="Electron" modified="2023-10-12T05:09:00.607Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.1.2 Chrome/106.0.5249.199 Electron/21.4.3 Safari/537.36" etag="Qd_x1J_gzjbS0l44e71w" version="21.1.2" type="device" pages="3">
<mxfile host="65bd71144e" pages="3">
<diagram name="Page-1" id="efa7a0a1-bf9b-a30e-e6df-94a7791c09e9">
<mxGraphModel dx="1434" dy="884" 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">
<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"/>
@@ -129,667 +129,708 @@
</mxGraphModel>
</diagram>
<diagram id="lCZzTbn_7m8qK95pbvvS" name="ER図">
<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">
<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;" vertex="1" parent="1">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-1">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-2">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-2">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-1">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-5">
<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="&lt;b style=&quot;border-color: var(--border-color); text-align: center; color: rgb(0, 51, 102);&quot;&gt;Tenant_id&lt;/b&gt;" 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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-5">
<mxCell id="inIyfaXWTM6ArMeTnGn6-7" value="&lt;b style=&quot;border-color: var(--border-color); text-align: center; color: rgb(0, 51, 102);&quot;&gt;Tenant_id&lt;/b&gt;" 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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-1">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-8">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-8">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-1">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-11">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-11">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-1">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-1">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-1">
<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;" vertex="1" parent="1">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-14">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-15">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-15">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-14">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-24">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-24">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-14">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-31">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-31">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-14">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-34">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-34">
<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;" vertex="1" parent="inIyfaXWTM6ArMeTnGn6-14">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-4">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-4">
<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="&lt;b style=&quot;border-color: var(--border-color); color: rgb(0, 51, 102);&quot;&gt;Tenant&lt;/b&gt;" 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;" vertex="1" parent="1">
<mxCell id="QUGX19b9cPh8sdE7zjoR-1" value="&lt;b style=&quot;border-color: var(--border-color); color: rgb(0, 51, 102);&quot;&gt;Tenant&lt;/b&gt;" 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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-1">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-2">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-2">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-1">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-5">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-5">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-1">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-8">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-8">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-1">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-11">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-11">
<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;" vertex="1" parent="QUGX19b9cPh8sdE7zjoR-1">
<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;" vertex="1" parent="xY3sbCmQmCiIU-0kb39k-5">
<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;" vertex="1" parent="xY3sbCmQmCiIU-0kb39k-5">
<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;" vertex="1" parent="1">
<mxGeometry x="420" y="480" width="180" height="240" as="geometry">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-1">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-2">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-2">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-1">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-56">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-56">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-1">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-5">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-5">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-1">
<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="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-8">
<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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-8">
<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="8Zu1yShcSHxMs68hy39H-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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-1">
<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="8Zu1yShcSHxMs68hy39H-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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-11">
<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="8Zu1yShcSHxMs68hy39H-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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-11">
<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="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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-1">
<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="XZEu5LJmFrV2HCpzu9aW-13" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="XZEu5LJmFrV2HCpzu9aW-12">
<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="XZEu5LJmFrV2HCpzu9aW-14" value="content" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="XZEu5LJmFrV2HCpzu9aW-12">
<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-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;" vertex="1" parent="8Zu1yShcSHxMs68hy39H-1">
<mxGeometry y="210" 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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-14">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-14">
<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;" vertex="1" parent="1">
<mxGeometry x="140" y="435" width="180" height="150" as="geometry">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-1">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-2">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-2">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-1">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-5">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-5">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-1">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-8">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-8">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-1">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-11">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-11">
<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;" edge="1" parent="1" source="GH2g80-cBXHe62XdPV4N-2" target="GH2g80-cBXHe62XdPV4N-14">
<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;" vertex="1" parent="1">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-18">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-34">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-34">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-18">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-51">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-51">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-18">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-25">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-25">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-18">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-28">
<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;" vertex="1" parent="GH2g80-cBXHe62XdPV4N-28">
<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;" vertex="1" parent="1">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-1">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-23">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-23">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-1">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-2">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-2">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-1">
<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="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-5">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-5">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-1">
<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="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-8">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-8">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-1">
<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="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-11">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-11">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-1">
<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="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-17">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-17">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-1">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-14">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-14">
<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;" vertex="1" parent="1">
<mxGeometry x="20" y="905" width="180" height="150" as="geometry" />
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-40">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-41">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-41">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-40">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-44">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-44">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-40">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-47">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-47">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-40">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-50">
<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;" vertex="1" parent="AJLbZMHdl7kzV18ppMoM-50">
<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;" vertex="1" parent="1">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-7">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-8">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-8">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-7">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-11">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-11">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-7">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-44">
<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;" vertex="1" parent="NCbWPNvujZOKFJAbQVH6-44">
<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;" edge="1" parent="1" source="QUGX19b9cPh8sdE7zjoR-2" target="inIyfaXWTM6ArMeTnGn6-2">
<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;" edge="1" parent="1" source="NCbWPNvujZOKFJAbQVH6-11" target="inIyfaXWTM6ArMeTnGn6-2">
<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;" edge="1" parent="1" source="inIyfaXWTM6ArMeTnGn6-15" target="NCbWPNvujZOKFJAbQVH6-44">
<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;" edge="1" parent="1" source="inIyfaXWTM6ArMeTnGn6-15" target="NCbWPNvujZOKFJAbQVH6-51">
<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;" edge="1" parent="1" source="NCbWPNvujZOKFJAbQVH6-51" target="8Zu1yShcSHxMs68hy39H-5">
<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;" edge="1" parent="1" source="8Zu1yShcSHxMs68hy39H-2" target="AJLbZMHdl7kzV18ppMoM-2">
<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>

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View 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&lt;br&gt;作成" 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>

Binary file not shown.

Binary file not shown.

View File

@@ -1,2 +1,3 @@
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/"

View File

@@ -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/"

View File

@@ -2,7 +2,6 @@
<html lang="ja-jp">
<head>
<title><%= productName %></title>
<meta charset="utf-8">
<meta name="description" content="<%= productDescription %>">
<meta name="format-detection" content="telephone=no">

View File

@@ -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,12 +10,16 @@
"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",
"@vueuse/core": "^10.9.0",
"axios": "^1.4.0",
"pinia": "^2.1.6",
"pinia": "^2.1.7",
"quasar": "^2.6.0",
"uuid": "^9.0.0",
"vue": "^3.0.0",

View 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>

View File

@@ -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,

View File

@@ -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

View File

@@ -0,0 +1,21 @@
// 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 }) => {
document.documentElement.lang="ja-JP";
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);
}
};
});

View File

@@ -1,12 +1,17 @@
<template>
<div class="q-pa-md">
<q-table 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>
@@ -17,23 +22,24 @@ export default {
name: 'actionSelect',
props: {
name: String,
type: String
type: String,
filter:String
},
setup() {
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/1').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,
@@ -41,7 +47,8 @@ export default {
selected: ref([]),
pagination:ref({
rowsPerPage:0
})
}),
isLoaded,
}
},
@@ -49,6 +56,7 @@ export default {
</script>
<style lang="scss">
.action-table{
height: 100%;
min-height: 10vh;
max-height: 68vh;
}
</style>

View File

@@ -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 {

View File

@@ -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',
props: {
name: String,
type: String
},
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});
type: String,
filter: String,
updateExternalSelectAppInfo: {
type: Function
}
)
},
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>

View File

@@ -0,0 +1,107 @@
<template>
<show-dialog v-model:visible="showflg" name="条件エディタ" @close="closeDg" min-width="50vw" 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>

View 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.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" min-width="400px">
<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;
margin: 0 2px;
}
.selected-obj{
margin: 0 2px;
}
</style>

View 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 q-my-xs">
<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;
margin: 0 2px;
}
.operator{
min-width: 150px;
max-height: 40px;
margin: 0 2px;
text-align: center;
font-size: 12pt;
}
</style>

View File

@@ -0,0 +1,63 @@
<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="sourceApp ? sourceApp :appId " :fields="sourceFields"></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, inject } 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) {
const selected = ref([]);
console.log(selected);
return {
sourceFields : inject('sourceFields'),
sourceApp : inject('sourceApp'),
tab: ref('fields'),
selected
}
},
}
</script>

View File

@@ -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">

View 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>

View 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>

View 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 lang="ts">
import { useAsyncState } from '@vueuse/core';
import { api } from 'boot/axios';
import { computed } from 'vue';
export default {
name: 'FieldList',
props: {
fields: Array,
name: String,
type: String,
appId: Number,
modelValue: Array,
filter: String
},
emits: [
'update:modelValue'
],
setup(props) {
// const rows = ref([]);
// const isLoaded = ref(false);
const columns = [
{ name: 'name', required: true, label: 'フィールド名', align: 'left', field: 'name', sortable: true },
{ name: 'code', label: 'フィールドコード', align: 'left', field: 'code', sortable: true },
{ name: 'type', label: 'フィールドタイプ', align: 'left', field: 'type', sortable: true }
]
const { state : rows, isReady: isLoaded, isLoading } = useAsyncState((args) => {
if (props.fields) {
return props.fields.map(f => ({ name: f.label, objectType: 'field', ...f }));
} else {
return api.get('api/v1/appfields', {
params: {
app: props.appId
}
}).then(res => {
console.log(res);
return Object.values(res.data.properties).map(f => ({ name: f.label, objectType: 'field', ...f }));
});
}
}, [{ name: '', objectType: '', type: '', code: '', label: '' }])
return {
columns,
rows,
// selected: ref([]),
isLoaded
}
},
}
</script>

View File

@@ -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: {
type: String,
appId:Number
default: 'single'
},
appId: Number,
not_page: {
type: Boolean,
default: false,
},
selectedFields:{
type:Array,
default:()=>[]
},
updateSelects: {
type: Function
},
filter: String,
},
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( () => {
api.get('appfields', {
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
}
}).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});
}
)
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
}
},

View File

@@ -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="min-width: 40vw; max-width: 80vw; max-height: 95vh;" :style="cardStyle">
<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',
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
}
},
}

View File

@@ -0,0 +1,60 @@
<template>
<div class="q-pa-md">
<q-table flat bordered row-key="id" :selection="type" :selected="modelValue"
@update:selected="$emit('update:modelValue', $event)" :columns="columns" :rows="rows" />
</div>
</template>
<script lang="ts">
import { PropType, reactive } from 'vue';
import { IActionVariable } from '../types/ActionTypes';
import { v4 as uuidv4 } from 'uuid';
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 variableName = (field) => {
const name = field.name;
return name.name;
}
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: variableName, required: true, sortable: true }
];
const rows = props.vars.flatMap((v) => {
if (v.name.vars && v.name.vars.length > 0) {
return v.name.vars
.filter(o => o.vName && o.logicalOperator && o.field)
.map(o => ({
id: uuidv4(),
objectType: 'variable',
name: { name: `${v.name.name}.${o.vName}` },
actionName: v.name.actionName,
displayName: v.name.displayName
}));
} else {
return [{ objectType: 'variable', ...v }];
}
});
return {
columns,
rows: reactive(rows)
}
}
}
</script>

View File

@@ -1,11 +1,5 @@
<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;
">
<div class="row app-box">
<q-icon
class="self-center q-ma-sm"
name="widgets"
@@ -13,7 +7,9 @@
style="font-size: 2em"
/>
<div class="col-7 self-center ellipsis">
{{ selectedApp.name }}
<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
@@ -26,8 +22,15 @@
></q-btn>
</div>
</div>
<ShowDialog v-model:visible="showSelectApp" name="アプリ" @close="closeDg">
<AppSelect ref="appDg" name="アプリ" type="single"></AppSelect>
<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>
@@ -37,6 +40,7 @@ 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:[
@@ -49,24 +53,22 @@ export default defineComponent({
setup(props, context) {
const store = useFlowEditorStore();
const authStore=useAuthStore();
const appDg = ref();
const showSelectApp=ref(false);
const selectedApp =ref<AppInfo>({
appId:"",
name:"",
});
const closeDg=(val :any)=>{
showSelectApp.value=false;
console.log("Dialog closed->",val);
if (val == 'OK') {
const data = appDg.value.selected[0];
console.log(data);
selectedApp.value={
const appInfo={
appId:data.id ,
name:data.name
};
store.setApp(selectedApp.value);
store.setFlow();
store.setApp(appInfo);
store.loadFlow();
}
}
const showAppDialog=()=>{
@@ -74,12 +76,19 @@ export default defineComponent({
}
return {
store,
selectedApp,
authStore,
showSelectApp,
showAppDialog,
closeDg,
appDg
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>

View File

@@ -1,14 +1,15 @@
<template>
<div class="q-pa-md q-gutter-sm">
<!-- <div class="q-pa-md q-gutter-sm"> -->
<q-tree
:nodes="store.eventTree.screens"
node-key="label"
node-key="eventId"
children-key="events"
no-connectors
v-model:expanded="expanded"
v-model:expanded="store.expandedScreen"
:dense="true"
:ref="tree"
>
<template v-slot:default-header="prop">
<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"
@@ -16,30 +17,78 @@
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>
</div>
<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 } from '../../types/KintoneEvents';
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 { S } from 'app/dist/spa/assets/QTable.50486f7c';
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([
store.currentFlow?.getRoot()?.title
]);
// 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;
@@ -48,24 +97,35 @@ export default defineComponent({
if(store.appInfo===undefined){
return;
}
const screen = store.eventTree.findScreen(node.eventId);
const screen = store.eventTree.findEventById(node.parentId);
let flow =store.findFlowByEventId(node.eventId);
const screenName=screen!==null?screen.label:"";
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,node.label)
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,
// expanded,
appDg,
tree,
showDialog,
isFieldChange,
onSelected,
selectedEvent,
addChangeEvent,
closeDg,
store
}
}
@@ -86,4 +146,6 @@ export default defineComponent({
.event-node:hover{
background-color: $light-blue-1;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="row justify-center" :style="{ marginLeft: node.inputPoint !== '' ? '240px' : '' }" >
<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" >
@@ -8,6 +8,10 @@
<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>
@@ -25,8 +29,12 @@
</q-btn>
</q-toolbar>
<q-separator />
<q-card-section>
<div class="text-h7">{{ node.title }}</div>
<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 />
@@ -40,23 +48,34 @@
</div>
</div>
<template v-if="hasBranch">
<div class="row justify-center" :style="{ marginLeft: node.inputPoint !== '' ? '240px' : '' }">
<div v-for="(point, index) in node.outputPoints" :key="index">
<node-line :action-node="node" :mode="getMode(point)" @addNode="addNode" :input-point="point"></node-line>
<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" :style="{ marginLeft: node.inputPoint !== '' ? '240px' : '' }">
<node-line :action-node="node" :mode="getMode('')" @addNode="addNode" input-point=""></node-line>
<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 } from '../../types/ActionTypes';
import { IActionNode, IActionProperty } from '../../types/ActionTypes';
import NodeLine, { Direction } from '../main/NodeLine.vue';
import { useFlowEditorStore } from 'stores/flowEditor';
export default defineComponent({
name: 'NodeItem',
components: {
@@ -77,8 +96,10 @@ export default defineComponent({
"nodeEdit",
"deleteNode",
"deleteAllNextNodes",
"copyFlow"
],
setup(props, context) {
const store = useFlowEditorStore();
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
const nodeStyle = computed(() => {
return {
@@ -87,23 +108,11 @@ export default defineComponent({
'selected': props.isSelected && !props.actionNode.isRoot
};
});
const getMode = (point: string) => {
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 nextNode=(point:string)=>{
const nextId= props.actionNode.nextNodeIds.get(point);
if(!nextId) return undefined;
return store.currentFlow?.findNodeById(nextId);
}
/**
* アクションノード追加イベントを
@@ -112,6 +121,38 @@ export default defineComponent({
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;
}
});
/**
* ノード選択状態
*/
@@ -119,9 +160,20 @@ export default defineComponent({
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);
}
/**
* ノードを削除する
*/
@@ -129,30 +181,68 @@ export default defineComponent({
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.name;
};
const copyFlow=()=>{
context.emit('copyFlow', props.actionNode);
}
return {
store,
node: props.actionNode,
nextNode,
isRoot: props.actionNode.isRoot,
hasBranch,
nodeStyle,
getMode,
// getMode,
addNode,
addNodeFromItem,
onNodeClick,
onNodeSelected,
onEditNode,
onNodeEdit,
onDeleteNode,
onDeleteAllNode
onDeleteNodeFromItem,
onDeleteAllNode,
onDeleteAllNextNodes,
copyFlow,
varName,
leftColumns,
rightColumns
}
}
});
</script>
<style lang="scss">
.action-node {
min-width: 300px !important;
min-width: 280px !important;
}
.action-title{
max-width: 280px !important;
overflow-wrap: anywhere;
}
.line {

View File

@@ -1,11 +1,28 @@
<template>
<div>
<svg class="node-line">
<polyline :points="points.linePoints" class="line" ></polyline>
<text class="add-icon" @click="addNode(node)" :x="points.iconPoint.x" :y="points.iconPoint.y" font-family="Arial" font-size="25"
<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>
@@ -27,55 +44,97 @@ export default defineComponent({
type: Object as PropType<IActionNode>,
required: true
},
mode: {
type: String as PropType<Direction>,
required: true
leftColumns:{
type:Number,
required:false
},
inputPoint:{
type:String
rightColumns:{
type:Number,
required:false
}
},
emits: ['addNode'],
setup(props,context) {
const hasBranch = computed(() => props.actionNode.outputPoints.length > 0);
const points = computed(() => {
switch (props.mode) {
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: '180, 0, 180, 40, 120, 40, 120, 60',
iconPoint: { x: 180, y: 20 }
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: '60, 0, 60, 40, 120, 40, 120, 60',
iconPoint: { x: 60, y: 20 }
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: '180, 0, 180, 40',
iconPoint: { x: 180, y: 20 }
linePoints: `${startX}, 0, ${startX}, 40`,
iconPoint: { x: startX, y: 20 }
};
case Direction.RightNotNext:
startX = ((leftColumn+rightColumn)/2.0 + 0.25)*300;
return {
linePoints: '60, 0, 60, 40',
iconPoint: { x: 60, y: 30 }
linePoints: `${startX}, 0, ${startX}, 40`,
iconPoint: { x: startX, y: 20 }
};
default:
return {
linePoints: '120, 0, 120, 60',
iconPoint: { x: 120, y: 30 }
linePoints: '150, 0, 150, 60',
iconPoint: { x: 150, y: 30 }
};
}
});
const addNode=(prveNode:IActionNode)=>{
context.emit('addNode',props.inputPoint);
};
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
addNode,
viewBox
}
}
});

View File

@@ -1,25 +1,27 @@
<template>
<div>
<div v-for="(item, index) in componentData" :key="index">
<component :is="item.component" v-bind="item.props" v-model="item.props.modelValue"></component>
<component :is="item.component" v-bind="item.props" :connectProps="connectProps" v-model="item.props.modelValue"></component>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
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
FieldInput,
EventSetter
},
props: {
jsonData: {
@@ -31,27 +33,43 @@ export default defineComponent({
required: false,
}
},
computed: {
componentData() {
return this.jsonData.elements.map((element: any) => {
if(this.jsonValue != undefined )
setup(props){
const componentData=computed<Array<IActionProperty>>(()=>{
return props.jsonData.elements.map((element: any) => {
if(props.jsonValue != undefined )
{
if(this.jsonValue.hasOwnProperty(element.props.name))
if(props.jsonValue.hasOwnProperty(element.props.name))
{
element.props.modelValue = this.jsonValue[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>

View 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: 'AppFieldSelect',
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>

View 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>

View File

@@ -0,0 +1,134 @@
<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 { ConditionNode, ConditionTree, Operator } from 'app/src/types/Conditions';
import { computed, defineComponent, provide, reactive, ref, watchEffect } from 'vue';
import ConditionEditor from '../ConditionEditor/ConditionEditor.vue';
type Props = {
props?: {
name: string;
modelValue?: {
fields: {
type: string;
label: string;
code: string;
}[]
}
}
};
export default defineComponent({
name: 'FieldInput',
inheritAttrs: false,
components: {
ConditionEditor
},
props: {
context: {
type: Array<Props>,
default: '',
},
displayName: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
hint: {
type: String,
default: '',
},
modelValue: {
type: String,
default: null
},
sourceType: {
type: String,
default: 'field'
}
},
setup(props, { emit }) {
const source = props.context.find(element => element?.props?.name === 'sources')
if (source) {
if(props.sourceType === 'field'){
provide('sourceFields', computed( () => source.props?.modelValue?.fields ?? []));
} else if(props.sourceType === 'app'){
console.log('sourceApp', source.props?.modelValue);
provide('sourceApp', computed( () => source.props?.modelValue?.app?.id));
}
}
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>

View File

@@ -0,0 +1,229 @@
<template>
<div>
<q-field :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="() => { dgIsShow = true }">クリックで設定</q-btn>
</q-card-actions>
<q-card-section class="text-caption">
<div v-if="processingObjectsInputDisplay && processingObjectsInputDisplay.length>0">
<div v-for="(item) in processingObjectsInputDisplay" :key="item">{{ item }}</div>
</div>
<div v-else>{{ placeholder }}</div>
</q-card-section>
</q-card>
</template>
</q-field>
<show-dialog v-model:visible="dgIsShow" name="集計処理" @close="closeDg" min-width="50vw" min-height="60vh">
<div class="q-mx-md q-mb-md">
<q-input v-model="processingProps.name" type="text" label-color="primary" label="集計結果の変数名"
placeholder="集計結果を格納する変数名を入力してください" stack-label />
</div>
<div class="q-mx-md">
<div class="row q-col-gutter-x-xs flex-center">
<div class="col-5">
<div class="q-mx-xs">データソース</div>
</div>
<div class="col-2">
<div class="q-mx-xs">集計計算</div>
</div>
<div class="col-4">
<div class="q-mx-xs">集計結果変数名</div>
</div>
<div class="col-1"><q-btn flat round dense icon="add" size="sm" @click="addProcessingObject" />
</div>
</div>
<div class="q-my-sm" v-for="(item, index) in processingObjects" :key="item.id">
<div class="row q-col-gutter-x-xs flex-center">
<div class="col-5">
<ConditionObject v-model="item.field" />
</div>
<div class="col-2">
<q-select v-model="item.logicalOperator" :options="logicalOperators" outlined dense></q-select>
</div>
<div class="col-4">
<q-input v-model="item.vName" type="text" outlined dense />
</div>
<div class="col-1">
<q-btn flat round dense icon="delete" size="sm" @click="() => deleteProcessingObject(index)" />
</div>
</div>
</div>
</div>
</show-dialog>
</div>
</template>
<script lang="ts">
import { v4 as uuidv4 } from 'uuid';
import { computed, defineComponent, provide, reactive, ref, watchEffect } from 'vue';
import ConditionObject from '../ConditionEditor/ConditionObject.vue';
import ShowDialog from '../ShowDialog.vue';
type Props = {
props?: {
name: string;
modelValue?: {
fields: {
type: string;
label: string;
code: string;
}[]
} | string
}
};
type ProcessingObjectType = {
field?: {
name: string | {
name: string;
};
objectType: string;
type: string;
code: string;
label: string;
noLabel: boolean;
};
logicalOperator?: string;
vName?: string;
id: string;
}
type ValueType = {
name: string;
actionName: string,
displayName: string,
vars: ProcessingObjectType[];
}
export default defineComponent({
name: 'DataProcessing',
inheritAttrs: false,
components: {
ShowDialog,
ConditionObject,
},
props: {
context: {
type: Array<Props>,
default: '',
},
displayName: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
modelValue: {
type: Object as () => ValueType,
},
placeholder: {
type: String,
default: '',
},
},
setup(props, { emit }) {
const source = props.context.find(element => element?.props?.name === 'sources')
if (source) {
provide('sourceFields', computed(() => {
const modelValue = source.props?.modelValue;
if (modelValue && typeof modelValue !== 'string') {
return modelValue.fields;
}
return null;
}));
}
const actionName = props.context.find(element => element?.props?.name === 'displayName')
const processingProps: ValueType = props.modelValue && props.modelValue.vars
? props.modelValue
: reactive({
name: '',
actionName: actionName?.props?.modelValue as string,
displayName: '結果(戻り値)',
vars: [{ id: uuidv4() }]
});
const closeDg = () => {
emit('update:modelValue', processingProps
);
}
const processingObjects = processingProps.vars;
const deleteProcessingObject = (index: number) => processingObjects.length === 1
? processingObjects.splice(0, processingObjects.length, { id: uuidv4() })
: processingObjects.splice(index, 1);
const processingObjectsInputDisplay = computed(() =>
processingObjects ?
processingObjects
.filter(item => item.field && item.logicalOperator && item.vName)
.map(item => {
const name = typeof item.field?.name === 'string'
? item.field.name
: item.field?.name.name;
return item.logicalOperator.operator!==''?
`${processingProps.name}.${item.vName} = ${item.logicalOperator.operator}(${name})`
:`${processingProps.name}.${item.vName} = ${name}`
})
: []
);
//集計処理方法
const logicalOperators = ref([
{
"operator": "",
"label": "なし"
},
{
"operator": "SUM",
"label": "合計"
},
{
"operator": "AVG",
"label": "平均"
},
{
"operator": "MAX",
"label": "最大値"
},
{
"operator": "MIN",
"label": "最小値"
},
{
"operator": "COUNT",
"label": "カウント"
},
{
"operator": "FIRST",
"label": "最初の値"
}
]);
watchEffect(() => {
emit('update:modelValue', processingProps);
});
return {
uuidv4,
dgIsShow: ref(false),
closeDg,
processingObjects,
processingProps,
addProcessingObject: () => processingObjects.push({ id: uuidv4() }),
deleteProcessingObject,
logicalOperators,
processingObjectsInputDisplay,
};
},
});
</script>
<style lang="scss"></style>

View File

@@ -1,6 +1,6 @@
<template>
<q-input v-model="selectedDate" :label="placeholder" mask="date" :rules="['date']">
<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">
@@ -13,6 +13,7 @@
</q-icon>
</template>
</q-input>
</div>
</template>
<script lang="ts">
@@ -20,11 +21,24 @@ 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: '',

View 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>

View File

@@ -1,34 +1,67 @@
<template>
<q-input v-model="selectedField" :label="placeholder">
<template v-slot:append>
<q-icon name="search" class="cursor-pointer" @click="showDg"/>
<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>
</q-input>
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeDg">
<!-- <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} from 'vue';
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: '',
},
modelValue: {
hint: {
type: String,
default: '',
},
modelValue: {
type: Object,
default: null
},
},
setup(props, { emit }) {
@@ -36,6 +69,9 @@ export default defineComponent({
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;
@@ -43,7 +79,7 @@ export default defineComponent({
const closeDg = (val: string) => {
if (val == 'OK') {
selectedField.value = appDg.value.selected[0].name;
selectedField.value = appDg.value.selected[0];
}
};
@@ -58,6 +94,7 @@ export default defineComponent({
showDg,
closeDg,
selectedField,
isSelected
};
}
});

View File

@@ -1,33 +1,99 @@
<template>
<q-input :label="placeholder" v-model="inputValue"/>
<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 { defineComponent,ref,watchEffect } from 'vue';
import { kMaxLength } from 'buffer';
import { defineComponent, ref, watchEffect, computed } from 'vue';
export default defineComponent({
name: 'InputText',
inheritAttrs: false,
props: {
displayName: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
modelValue: {
hint: {
type: String,
default: '',
},
maxLength: {
type: Number,
default: undefined
},
//例:[val=>!!val ||'入力してください']
rules: {
type: String,
default: undefined
},
modelValue: {
// type: Any,
default: '',
},
},
setup(props, { emit }) {
const inputValue = ref(props.modelValue);
watchEffect(() => {
emit('update:modelValue', inputValue.value);
const inputValue = computed({
get: () => {
if (props.modelValue !== null && typeof props.modelValue === 'object' && 'name' in props.modelValue) {
return props.modelValue.name;
} else {
return props.modelValue;
}
},
set: (val) => {
if (props.name === 'verName') {
// return props.modelValue.name;
emit('update:modelValue', { name: val });
} else {
emit('update:modelValue', val);
}
},
});
// const inputValue = ref(props.modelValue);
const rulesExp = props.rules === undefined ? null : eval(props.rules);
// const finalValue = computed(() => {
// return props.name !== 'verName' ? inputValue.value : {
// name: inputValue.value,
// };
// });
// watchEffect(() => {
// emit('update:modelValue', finalValue);
// });
return {
inputValue,
showhint: ref(false),
rulesExp
};
},
});
</script>
<style lang="scss">
.hint-text {
white-space: always;
max-width: 450px;
font-size: 1.2em;
}
</style>

View 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>

View 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>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div v-for="(item, index) in properties" :key="index" >
<component :is="item.component" v-bind="item.props" v-model="item.props.modelValue"></component>
<component :is="item.component" v-bind="item.props" :context="properties" :connectProps="connectProps(item.props)" v-model="item.props.modelValue"></component>
</div>
</div>
</template>
@@ -15,7 +15,14 @@ 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 { IActionNode,IActionProperty } from 'src/types/ActionTypes';
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 DataProcessing from './DataProcessing.vue';
import { IActionNode,IActionProperty,IProp } from 'src/types/ActionTypes';
export default defineComponent({
name: 'PropertyList',
@@ -23,7 +30,14 @@ export default defineComponent({
InputText,
SelectBox,
DatePicker,
FieldInput
FieldInput,
AppFieldSelect,
MuiltInputText,
ConditionInput,
EventSetter,
ColorPicker,
NumInput,
DataProcessing
},
props: {
nodeProps: {
@@ -36,10 +50,25 @@ export default defineComponent({
}
},
setup(props, context) {
const properties=ref(props.nodeProps)
const properties=ref(props.nodeProps);
const connectProps=(props:IProp)=>{
const connProps:any={context:properties};
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
properties,
connectProps
}
}
});
</script>
<style lang="scss">
</style>

View File

@@ -11,24 +11,24 @@
elevated
overlay
>
<q-card class="column full-height" style="width: 300px">
<q-card class="column" style="max-width: 300px;min-height: 100%">
<q-card-section>
<div class="text-h6">プロパティ</div>
<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="Save" @click="save"/>
<q-btn flat label="Cancel" @click="cancel" />
<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 { reactive, ref,defineComponent, defineProps,PropType ,watchEffect} from 'vue'
import { ref,defineComponent, PropType ,watchEffect} from 'vue'
import PropertyList from 'components/right/PropertyList.vue';
import { IActionNode } from 'src/types/ActionTypes';
export default defineComponent({
@@ -47,24 +47,24 @@ import { IActionNode } from 'src/types/ActionTypes';
}
},
emits: [
"update:drawerRight"
'update:drawerRight'
],
setup(props,{emit}) {
const showPanel =ref(props.drawerRight);
const actionProps =ref(props.actionNode.actionProps);
const actionProps =ref(props.actionNode?.actionProps);
watchEffect(() => {
showPanel.value = props.drawerRight;
actionProps.value= props.actionNode.actionProps;
actionProps.value= props.actionNode?.actionProps;
});
const cancel = async() =>{
showPanel.value = false;
emit("update:drawerRight",false )
emit('update:drawerRight',false )
}
const save = async () =>{
showPanel.value=false;
emit("update:drawerRight",false )
emit('update:drawerRight',false )
}
return {

View File

@@ -1,13 +1,21 @@
<template>
<q-select v-model="selectedValue" :label="placeholder" :options="options"/>
<div v-bind="$attrs">
<q-select v-model="selectedValue" :use-chips="multiple" :label="displayName" label-color="primary" :options="options" stack-label
:multiple="multiple"/>
</div>
</template>
<script lang="ts">
import { defineComponent,ref,watchEffect } from 'vue';
import { defineComponent,ref,watchEffect,computed } from 'vue';
export default defineComponent({
name: 'SelectBox',
inheritAttrs:false,
props: {
displayName:{
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
@@ -16,20 +24,27 @@ export default defineComponent({
type: Array,
required: true,
},
modelValue: {
selectType:{
type:String,
default:'',
},
modelValue: {
type: Object,
default: null,
},
},
setup(props, { emit }) {
const selectedValue = ref(props.modelValue);
const multiple = computed(()=>{
return props.selectType==='multiple'
});
watchEffect(() => {
emit('update:modelValue', selectedValue.value);
});
return {
selectedValue
selectedValue,
multiple
};
},
});

View 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;
}
}
}

View File

@@ -6,32 +6,52 @@ export class FlowCtrl
async getFlows(appId:string):Promise<ActionFlow[]>
{
const result = await api.get(`http://127.0.0.1:8000/api/flows/${appId}`);
const flows:ActionFlow[]=[];
try{
const result = await api.get(`api/flows/${appId}`);
//console.info(result.data);
if(!result.data || !Array.isArray(result.data)){
return [];
}
const flows:ActionFlow[]=[];
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('http://127.0.0.1:8000/api/flow',jsonData);
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('http://127.0.0.1:8000/api/flow/' + jsonData.flowid,jsonData);
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;
}
}

View File

@@ -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;
}

View File

@@ -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[] = [
{
@@ -59,14 +65,14 @@ const essentialLinks: EssentialLinkProps[] = [
title: 'フローエディター',
caption: 'flowChart',
icon: 'account_tree',
link: '/#/flowEditor2',
link: '/#/FlowChart',
target:'_self'
},
{
title: 'FlowEditor',
caption: 'FlowEditor',
icon: 'account_tree',
link: '/#/flowEditor',
title: '条件エディター',
caption: 'condition',
icon: 'tune',
link: '/#/condition',
target:'_self'
},
{
@@ -145,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>

View File

@@ -1,50 +1,50 @@
<template>
<div class="q-pa-md q-gutter-sm event-tree">
<q-drawer
side="left"
overlay
bordered
v-model="drawerLeft"
:show-if-above="false"
elevated
>
<!-- <q-card class="column full-height" style="width: 300px">
<q-card-section> -->
<div class="flex-center fixd-top" >
<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>
<!-- </q-card-section> -->
<q-separator />
<!-- <q-card-section> -->
<div class="flex-center">
<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-card-section> -->
<!-- </q-card> -->
</q-drawer>
</div>
<q-page>
<div class="q-pa-md q-gutter-sm">
<div class="flowchart" v-if="store.currentFlow">
<node-item v-for="(node,) in store.currentFlow.actionNodes" :key="node.id"
:isSelected="node===state.activeNode" :actionNode="node"
@addNode="addNode"
@nodeSelected="onNodeSelected"
@nodeEdit="onNodeEdit"
@deleteNode="onDeleteNode"
@deleteAllNextNodes="onDeleteAllNextNodes"
></node-item>
<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>
</q-page>
<PropertyPanel :actionNode="state.activeNode" v-model:drawerRight="drawerRight"></PropertyPanel>
<ShowDialog v-model:visible="showAddAction" name="アクション" @close="closeDg">
<action-select ref="appDg" name="model" type="single"></action-select>
<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">
@@ -52,6 +52,8 @@ 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';
@@ -59,30 +61,44 @@ 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';
const drawerLeft = ref(true);
import { useQuasar } from 'quasar';
const deployLoading = ref(false);
const saveLoading = ref(false);
const drawerLeft = ref(false);
const $q = useQuasar();
const store = useFlowEditorStore();
// ref関数を使ってtemplateとバインド
const state=reactive({
activeNode:{
id:""
},
})
const authStore = useAuthStore();
const appDg = ref();
const prevNodeIfo = ref({
prevNode: {} as IActionNode,
inputPoint: ""
});
const refFlow = ref<ActionFlow|null>(null);
// 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;
@@ -90,39 +106,128 @@ const addNode=(node:IActionNode,inputPoint:string)=>{
const onNodeSelected = (node: IActionNode) => {
//右パネルが開いている場合、自動閉じる
if(drawerRight.value && state.activeNode.id!==node.id){
if (drawerRight.value && store.activeNode?.id !== node.id) {
drawerRight.value = false;
}
state.activeNode = node;
store.setActiveNode(node);
}
const onNodeEdit = (node: IActionNode) => {
state.activeNode = node;
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.content);
const action = new ActionNode(data.name,data.desc,"",[],actionProps);
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 () => {
const flowCtrl = new FlowCtrl();
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);
@@ -130,31 +235,40 @@ const fetchData= async ()=>{
if (actionFlows && actionFlows.length == 1) {
store.selectFlow(actionFlows[0]);
}
refFlow.value=actionFlows[0];
const root =refFlow.value.getRoot();
const root = actionFlows[0].getRoot();
if (root) {
state.activeNode=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%;
}
.flow-container{
height: 91.5dvb;
overflow: hidden;
}
.event-tree .q-drawer {
top: 50px;
z-index: 999;
}
.expand{
position: fixed;
left: 0px;
top: 50%;
z-index: 9999;
}
</style>

View File

@@ -13,7 +13,7 @@
</div>
</q-page>
<PropertyPanel :actionNode="state.activeNode" v-model:drawerRight="drawerRight"></PropertyPanel>
<show-dialog v-model:visible="showAddAction" name="アクション" @close="closeDg">
<show-dialog v-model:visible="showAddAction" name="アクション" @close="closeDg" width="350px">
<action-select ref="appDg" name="アクション" type="single"></action-select>
</show-dialog>
</template>

View File

@@ -1,117 +0,0 @@
<template>
<div >
<div class="q-ma-md">
<div class="q-gutter-xs row items-start">
<q-btn
size="md"
@click="drawerLeft = !drawerLeft"
icon="keyboard_double_arrow_right"
round
/>
<q-space />
<q-btn
color="white"
size="sm"
text-color="black"
label="キャンセル"
dense
/>
<q-btn
class="q-px-sm"
color="primary"
size="sm"
label="保存する"
@click="save()"
dense
/>
</div>
</div>
<q-layout
container
class="flow-container 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">
<ItemSelector />
</div>
</q-card-section>
</div>
<q-separator />
<div class="q-mt-md q-pa-sm">
<q-card-section>
<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';
import { FlowCtrl } from '../control/flowctrl'
const flowCtrl = new FlowCtrl();
const actName = ref('勤怠管理 - 4');
const drawerLeft = ref(false);
const store = useFlowEditorStore();
const { flowNames1 } = storeToRefs(store);
let isNew = ref(true);
const save = () =>{
if(isNew.value)
{
flowCtrl.SaveFlow({appid:'1',flowid:'flow123',eventid:'event123',name:'test',content:'[]'});
isNew.value = false;
}
else
{
flowCtrl.UpdateFlow({appid:'1',flowid:'flow123',eventid:'event123',name:'test',content:'[{"a":"b"}]'});
}
}
</script>
<style lang="scss">
.flow-toolbar{
opacity: 50%;
}
.flow-container{
height: calc(91.5dvb - 50px);
overflow: hidden;
}
</style>

View 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>

View File

@@ -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>

View 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>

View 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>

View 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>

View File

@@ -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>

View File

@@ -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,12 +17,11 @@ 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 Router = createRouter({
const routerInstance = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
routes,
@@ -32,5 +31,46 @@ export default route(function (/* { store, ssrContext } */) {
history: createHistory(process.env.VUE_ROUTER_BASE),
});
return Router;
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;
// });

View File

@@ -1,6 +1,17 @@
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'),
@@ -9,11 +20,13 @@ const routes: RouteRecordRaw[] = [
{ path: 'ruleEditor', component: () => import('pages/RuleEditor.vue') },
{ path: 'test', component: () => import('pages/testQursar.vue') },
{ path: 'flow', component: () => import('pages/testFlow.vue') },
{ path: 'flowchart', component: () => import('pages/FlowChartTest.vue') },
{ path: 'FlowChartTest', component: () => import('pages/FlowChartTest.vue') },
{ path: 'flowEditor', component: () => import('pages/FlowEditorPage.vue') },
{ path: 'flowEditor2', component: () => import('pages/FlowChart.vue') },
{ path: 'flowChart2', component: () => import('pages/FlowEditorPage2.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') }
],
},
// Always leave this as last one,

View File

@@ -1,6 +1,6 @@
import { defineStore } from 'pinia';
import { AppInfo ,IActionFlow} from 'src/types/ActionTypes';
import { kintoneEvents,IKintoneEvent,KintoneEventManager } from 'src/types/KintoneEvents';
import { AppInfo ,IActionFlow, IActionNode} from 'src/types/ActionTypes';
import { IKintoneEvent,KintoneEventManager } from 'src/types/KintoneEvents';
import {FlowCtrl } from '../control/flowctrl';
export interface FlowEditorState{
@@ -8,18 +8,23 @@ export interface FlowEditorState{
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,
eventTree:kintoneEvents,
selectedEvent:undefined
activeNode:undefined,
eventTree:eventTree,
selectedEvent:undefined,
expandedScreen:[]
}),
getters: {
/**
@@ -51,20 +56,62 @@ export const useFlowEditorStore = defineStore("flowEditor",{
selectFlow(flow:IActionFlow){
this.selectedFlow=flow;
},
setActiveNode(node:IActionNode){
this.activeNode=node;
},
setApp(app:AppInfo){
this.appInfo=app;
},
async setFlow(){
/**
* DBからフルーを保存する
* @returns
*/
async loadFlow(){
if(this.appInfo===undefined) return;
const actionFlows = await flowCtrl.getFlows(this.appInfo?.appId);
//eventTreeにバンドする
this.eventTree.bindFlows(actionFlows);
if(actionFlows && actionFlows.length>0){
this.setFlows(actionFlows);
if(actionFlows===undefined || actionFlows.length===0){
this.flows=[];
this.selectedFlow=undefined;
return;
}
if(actionFlows && actionFlows.length==1){
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);
}
}

View File

@@ -0,0 +1,91 @@
import { defineStore } from 'pinia';
import { api } from 'boot/axios';
import {router} from 'src/router';
import {IDomainInfo} from '../types/ActionTypes';
export interface IUserState{
token?:string;
returnUrl:string;
currentDomain:IDomainInfo;
LeftDrawer:boolean;
}
export const useAuthStore = defineStore({
id: 'auth',
state: ():IUserState =>{
const token=localStorage.getItem('token')||'';
if(token!==''){
api.defaults.headers["Authorization"]='Bearer ' + token;
}
return {
token,
returnUrl: '',
LeftDrawer:false,
currentDomain: JSON.parse(localStorage.getItem('currentDomain')||"{}")
}
},
getters:{
toggleLeftDrawer():boolean{
return this.LeftDrawer;
}
},
actions: {
toggleLeftMenu(){
this.LeftDrawer=!this.LeftDrawer;
},
async login(username:string, password:string) {
const params = new URLSearchParams();
params.append('username', username);
params.append('password', password);
try{
const result = await api.post(`api/token`,params);
console.info(result);
this.token =result.data.access_token;
localStorage.setItem('token', result.data.access_token);
api.defaults.headers["Authorization"]='Bearer ' + this.token;
this.currentDomain=await this.getCurrentDomain();
localStorage.setItem('currentDomain',JSON.stringify(this.currentDomain));
this.router.push(this.returnUrl || '/');
return true;
}catch(e)
{
console.info(e);
return false;
}
},
async getCurrentDomain():Promise<IDomainInfo>{
const activedomain = await api.get(`api/activedomain`);
return {
id:activedomain.data.id,
domainName:activedomain.data.name,
kintoneUrl:activedomain.data.url
}
},
async getUserDomains():Promise<IDomainInfo[]>{
const resp = await api.get(`api/domain`);
const domains =resp.data as any[];
return domains.map(data=>{
return {
id:data.id,
domainName:data.name,
kintoneUrl:data.url
}
});
},
logout() {
this.token = undefined;
localStorage.removeItem('token');
localStorage.removeItem('currentDomain');
router.push('/login');
},
async setCurrentDomain(domain:IDomainInfo){
if(domain.id===this.currentDomain.id){
return;
}
await api.put(`api/activedomain/${domain.id}`);
this.currentDomain=domain;
localStorage.setItem('currentDomain',JSON.stringify(this.currentDomain));
}
}
});

View File

@@ -1,4 +1,10 @@
import { v4 as uuidv4 } from 'uuid';
export interface IDomainInfo{
id:number;
domainName:string;
kintoneUrl:string;
}
/**
* アプリ情報
*/
@@ -10,18 +16,44 @@ export interface AppInfo {
}
/**
* アクションのプロパティ定義
* 属性項目情報
*/
export interface IActionProperty {
component: string;
props: {
export interface IProp{
//プロパティ名
name: string;
//プロパティ表示名
displayName: string;
placeholder: string;
//入力提示・説明
hint:string;
//関連属性リスト
connectProps:[{key:string,propName:string}]|undefined;
//プロパティ設定値
modelValue: any;
}
/**
* アクションのプロパティ定義
*/
export interface IActionProperty {
component: string;
props: IProp;
}
/**
* 変数オブジェクト
*/
export interface IActionVariable{
actionName:string;
displayName:string;
name: {
name:string;
actionName:string;
displayName:string;
vars : {
vName:string;
logicalOperator:string;
field: object;
}[]
};
}
/**
@@ -32,6 +64,7 @@ export interface IActionNode{
//アクション名
name: string;
title: string;
varName:IProp|undefined;
subTitle: string;
inputPoint: string;
//出力ポイント(条件分岐以外未使用)
@@ -50,8 +83,16 @@ export interface IActionNode{
*/
export interface IActionFlow {
id: string;
actionNodes:Array<IActionNode>;
actionNodes: IActionNode[];
addNode(newNode: IActionNode, prevNode?: IActionNode, inputPoint?: string): IActionNode;
removeNode(targetNode: IActionNode): boolean;
removeAllNext(targetNodeId: string): void;
getVarNames(currentNode:IActionNode):IActionVariable[];
findNodeById(id: string): IActionNode | undefined;
toJSON(): any;
getRoot(): IActionNode | undefined;
createNewId(): string;
getColumns(node:IActionNode):number;
}
/**
@@ -59,18 +100,10 @@ export interface IActionFlow {
*/
class ActionProperty implements IActionProperty {
component: string;
props: {
// プロパティ名
name: string;
// プロパティ表示名
displayName: string;
placeholder: string;
// プロパティ設定値
modelValue: any;
};
props: IProp;
static defaultProperty(): IActionProperty {
return new ActionProperty('InputText','displayName','表示名','表示を入力してください','');
return new ActionProperty('InputText', 'displayName', '表示名', '表示を入力してください', '','');
};
constructor(
@@ -78,6 +111,7 @@ class ActionProperty implements IActionProperty {
name: string,
displayName: string,
placeholder: string,
hint:string,
modelValue: any
) {
this.component = component;
@@ -85,6 +119,8 @@ class ActionProperty implements IActionProperty {
name: name,
displayName: displayName,
placeholder: placeholder,
hint:hint,
connectProps:undefined,
modelValue: modelValue
};
}
@@ -97,10 +133,20 @@ class ActionProperty implements IActionProperty {
export class ActionNode implements IActionNode {
id: string;
name: string;
title:string;
get title(): string {
const prop = this.actionProps.find((prop) => prop.props.name === "displayName");
return prop?.props.modelValue;
};
get subTitle(): string {
return this.name;
};
//変数名
get varName():IProp|undefined{
const prop = this.actionProps.find((prop) => prop.props.name === "verName");
return prop?.props;
}
inputPoint: string;
//出力ポイント(条件分岐以外未使用)
outputPoints: Array<string>;
@@ -126,11 +172,10 @@ export class ActionNode implements IActionNode {
) {
this.id = uuidv4();
this.name = name;
this.title=title;
this.inputPoint = inputPoint;
this.outputPoints = outputPoint;
const defProp = ActionProperty.defaultProperty();
defProp.props.displayName=title;
defProp.props.modelValue = title;
this.actionProps = actionProps;
const prop = this.actionProps.find((prop) => prop.props.name === defProp.props.name);
if (prop === undefined) {
@@ -149,6 +194,7 @@ export class RootAction implements IActionNode {
title: string;
subTitle: string;
inputPoint: string;
varName: IProp | undefined=undefined;
//出力ポイント(条件分岐以外未使用)
outputPoints: Array<string>;
isRoot: boolean;
@@ -186,7 +232,8 @@ export class ActionFlow implements IActionFlow {
} else {
this.actionNodes = [actionNodes];
}
this.id=uuidv4();
this.id = '';
//this.id = uuidv4();
}
/**
* ノードを追加する
@@ -200,13 +247,12 @@ export class ActionFlow implements IActionFlow {
addNode(
newNode: IActionNode,
prevNode?: IActionNode,
inputPoint?:string):IActionNode
{
inputPoint?: string): IActionNode {
if (inputPoint !== undefined) {
newNode.inputPoint = inputPoint;
}
if (prevNode !== undefined) {
this.connectNodes(prevNode,newNode,inputPoint||'');
this.resetNodeRelation(prevNode, newNode, inputPoint || '');
} else {
prevNode = this.actionNodes[this.actionNodes.length - 1];
this.connectNodes(prevNode, newNode, inputPoint || '');
@@ -250,9 +296,29 @@ removeAllNext(targetNodeId :string){
return false;
}
for (const [, id] of targetNode.nextNodeIds) {
this.removeAllNext(id);
this.removeFromActionNodes(id);
this.removeAll(id);
}
targetNode.nextNodeIds.clear();
}
/***
* 目標ノードの次のノードを全部削除する
*/
removeAll(targetNodeId: string) {
if (!targetNodeId || targetNodeId === '') {
return;
}
const targetNode = this.findNodeById(targetNodeId);
if (!targetNode) {
return
}
if (targetNode.nextNodeIds.size == 0) {
return
}
for (const [, id] of targetNode.nextNodeIds) {
this.removeAll(id);
}
this.removeNode(targetNode);
}
// 断开与前一个节点的连接
@@ -270,7 +336,7 @@ disconnectFromPrevNode(targetNode: IActionNode): void {
}
}
// actionNodes 数组中移除节点
// actionNodes からノードを削除する
private removeFromActionNodes(targetNodeId: string): void {
const index = this.actionNodes.findIndex(node => node.id === targetNodeId);
if (index > -1) {
@@ -300,8 +366,7 @@ reconnectOrRemoveNextNodes(targetNode: IActionNode): void {
if (!nextNodeId) return;
const nextNode = this.findNodeById(nextNodeId);
if (!nextNode) return;
nextNode.prevNodeId=prevNode.id;
prevNode.nextNodeIds.set(targetNode.inputPoint||'',nextNodeId);
this.connectNodes(prevNode,nextNode,targetNode.inputPoint || '');
return;
}
//二つ以上の場合
@@ -333,14 +398,19 @@ reconnectOrRemoveNextNodes(targetNode: IActionNode): void {
return true;
}
/**
*
* @param prevNode ノード挿入時の接続をリセットする
* @param newNode
* @param inputPoint
*/
resetNodeRelation(prevNode: IActionNode, newNode: IActionNode, inputPoint?: string) {
// 设置新节点和前节点的关联
//元の次のノードを取得
const originalNextNodeId = prevNode.nextNodeIds.get(inputPoint || '');
prevNode.nextNodeIds.set(inputPoint || '', newNode.id);
newNode.prevNodeId = prevNode.id;
// 保存前节点原有的后节点ID
const originalNextNodeId = prevNode.nextNodeIds.get(inputPoint || '');
newNode.inputPoint=inputPoint||'';
this.setNewNodeNextId(newNode, originalNextNodeId, inputPoint);
}
/**
@@ -350,16 +420,19 @@ reconnectOrRemoveNextNodes(targetNode: IActionNode): void {
* @param inputPoint
*/
private setNewNodeNextId(newNode: IActionNode, originalNextNodeId: string | undefined, inputPoint?: string) {
// 如果原先的后节点存在
if (originalNextNodeId) {
// 检查新节点的 outputPoints 是否包含该 inputPoint
// 元の接続ノードが存在する
if (!originalNextNodeId) {return;}
const originNextNode = this.findNodeById(originalNextNodeId);
if (!originNextNode) {return;}
// 新しいノードの outputPoints に該当 inputPointが存在するか場合をチェックする
if (newNode.outputPoints.includes(inputPoint || '')) {
newNode.nextNodeIds.set(inputPoint || '', originalNextNodeId);
originNextNode.prevNodeId=newNode.id;
} else {
// 如果不包含,选择新节点的一个 outputPoint
// inputPointが存在しない場合、outputPointのポイントの任意ポートを選択する
const alternativeOutputPoint = newNode.outputPoints.length > 0 ? newNode.outputPoints[0] : '';
newNode.nextNodeIds.set(alternativeOutputPoint, originalNextNodeId);
}
originNextNode.prevNodeId=newNode.id;
}
}
@@ -370,6 +443,42 @@ reconnectOrRemoveNextNodes(targetNode: IActionNode): void {
return this.actionNodes.find((node) => node.id === id);
}
getVarNames(currentNode:IActionNode):IActionVariable[]{
let varNames:IActionVariable[]=[];
if(currentNode.prevNodeId!==undefined){
const prevNode=this.findNodeById(currentNode.prevNodeId);
if(prevNode!==undefined){
varNames = this.getPrevVarNames(prevNode);
}
}
return varNames;
}
getPrevVarNames(prevNode:IActionNode):IActionVariable[]{
let varNames:IActionVariable[]=[];
if(prevNode.varName!==undefined && prevNode.varName.modelValue){
if(prevNode.varName.modelValue ==='object'){
console.log(prevNode);
}
varNames.unshift({
actionName:prevNode.name,
displayName:prevNode.varName.displayName,
name:prevNode.varName.modelValue
});
}
if(prevNode.prevNodeId!==undefined){
const prevPrevNode=this.findNodeById(prevNode.prevNodeId);
if(prevPrevNode!==undefined){
const prevVars = this.getPrevVarNames(prevPrevNode);
varNames=[...prevVars,...varNames];
}
}
return varNames;
}
toJSON() {
return {
id: this.id,
@@ -377,23 +486,43 @@ reconnectOrRemoveNextNodes(targetNode: IActionNode): void {
const { nextNodeIds, ...rest } = node;
return {
...rest,
nextNodeIds: Array.from(nextNodeIds.entries())
nextNodeIds: Object.fromEntries(nextNodeIds)
};
})
};
}
getColumns(node:IActionNode):number{
let result= 1;
if(node.outputPoints && node.outputPoints.length>1){
result += node.outputPoints.length -1;
}
let nextNode;
for (const [key, id] of node.nextNodeIds) {
nextNode=this.findNodeById(id);
if(nextNode){
result +=this.getColumns(nextNode)-1;
}
}
return result;
}
getRoot(): IActionNode | undefined {
return this.actionNodes.find(node => node.isRoot)
}
createNewId():string{
this.id=uuidv4();
return this.id;
}
static fromJSON(json: string): ActionFlow {
const parsedObject = JSON.parse(json);
const actionNodes = parsedObject.actionNodes.map((node: any) => {
const nodeClass = !node.isRoot? new ActionNode(node.name,node.title,node.inputPoint,node.outputPoint,node.actionProps)
const nodeClass = !node.isRoot ? new ActionNode(node.name, node.title, node.inputPoint, node.outputPoints, node.actionProps)
: new RootAction(node.name, node.title, node.subTitle);
nodeClass.nextNodeIds=new Map(node.nextNodeIds);
nodeClass.nextNodeIds = new Map<string,string>(Object.entries(node.nextNodeIds));
nodeClass.prevNodeId = node.prevNodeId;
nodeClass.id = node.id;
return nodeClass;
@@ -402,7 +531,5 @@ reconnectOrRemoveNextNodes(targetNode: IActionNode): void {
actionFlow.id = parsedObject.id;
return actionFlow;
}
}

View File

@@ -0,0 +1,336 @@
//ノード種別
export enum NodeType{
Root = 'root',
LogicGroup ='logicgroup',
Condition ='condition'
}
//ロジックオペレーター
export enum LogicalOperator{
AND = 'AND',
OR = 'OR'
}
//条件オペレーター
export enum Operator{
Equal = '=',
NotEqual='!=',
Greater = '>',
GreaterOrEqual = '>=',
Less = '<',
LessOrEqual = '<=',
Contains = 'contains',
NotContains = 'not contains',
StartWith = 'start With',
EndWith = 'end with',
NotStartWith = 'not start with',
NotEndWith = 'not end with'
}
// INode
export interface INode {
index:number;
type: NodeType;
header:string;
parent: INode | null;
logicalOperator:LogicalOperator
}
// ロジックノード
export class GroupNode implements INode {
index:number;
type: NodeType;
children: INode[];
parent: INode | null;
logicalOperator: LogicalOperator;
get label():string{
return this.logicalOperator;
}
get header():string{
return this.type===NodeType.Root?'root':'generic';
}
get expanded():boolean{
return this.children.length>0;
}
constructor(logicOp:LogicalOperator, parent: INode | null) {
this.index=0;
this.type = parent==null?NodeType.Root: NodeType.LogicGroup;
this.logicalOperator = logicOp;
this.parent=parent;
this.children=[];
}
static fromJSON(json: any, parent: INode | null = null): GroupNode {
const node = new GroupNode(json.logicalOperator, parent);
node.index=json.index;
node.children = json.children.map((childJson: any) => {
return childJson.type === NodeType.LogicGroup
? GroupNode.fromJSON(childJson, node)
: ConditionNode.fromJSON(childJson, node);
});
return node;
}
}
// 条件式ノード
export class ConditionNode implements INode {
index: number;
type: NodeType;
parent:INode;
get logicalOperator(): LogicalOperator{
return this.parent.logicalOperator;
};
object: any; // 比較元
operator: Operator; // 比較子
value: any;
get header():string{
return 'generic';
}
constructor(object: any, operator: Operator, value: any, parent: GroupNode) {
this.index=0;
this.type = NodeType.Condition;
this.object = object;
this.operator = operator;
this.value = value;
this.parent=parent;
}
static fromJSON(json: any, parent: GroupNode): ConditionNode {
const node= new ConditionNode(
json.object,
json.operator,
json.value,
parent
);
node.index=json.index;
return node;
}
}
// 条件式の管理クラス
export class ConditionTree {
root: GroupNode;
maxIndex:number;
constructor() {
this.maxIndex=0;
this.root = new GroupNode(LogicalOperator.AND, null);
}
// ノード追加
addNode(parent: GroupNode, node: INode): void {
this.maxIndex++;
node.index=this.maxIndex;
parent.children.push(node);
}
// ノード削除
removeNode(node: INode): void {
if (node.parent === null) {
throw new Error('ルートノード削除できません');
} else {
const parent = node.parent as GroupNode;
const index = parent.children.indexOf(node);
if (index > -1) {
parent.children.splice(index, 1);
}
}
}
findByIndex(index:number):INode|undefined{
return this.findChildren(this.root,index);
}
findChildren(parent: GroupNode, index: number): INode | undefined {
if (parent.index === index) {
return parent;
}
for (const node of parent.children) {
if (node.index === index) {
return node;
}
if (node.type !== NodeType.Condition) {
const foundNode = this.findChildren(node as GroupNode, index);
if (foundNode) {
return foundNode;
}
}
}
return undefined;
}
getMaxIndex(node:INode):number{
let maxIndex:number=node.index;
if(node.type!==NodeType.Condition){
const groupNode = node as GroupNode;
groupNode.children.forEach((child)=>{
const childMax = this.getMaxIndex(child);
if(childMax>maxIndex){
maxIndex=childMax;
}
});
}
return maxIndex;
}
//条件式を表示する
buildConditionString(node:INode){
if (node.type !== NodeType.Condition) {
let conditionString = '(';
const groupNode = node as GroupNode;
for (let i = 0; i < groupNode.children.length; i++) {
const childConditionString = this.buildConditionString(groupNode.children[i]);
if (childConditionString !== '') {
conditionString += childConditionString;
if (i < groupNode.children.length - 1) {
conditionString += ` ${groupNode.logicalOperator} `;
}
}
}
conditionString += ')';
return conditionString;
} else {
const condNode=node as ConditionNode;
if (condNode.object && condNode.operator ) {
let value=condNode.value;
if(value && typeof value ==='object' && ('label' in value)){
value =condNode.value.label;
}
return `${typeof condNode.object.name === 'object' ? condNode.object.name.name : condNode.object.name} ${condNode.operator} '${value}'`;
} else {
return '';
}
}
}
/**
*
* @param node ノード移動
* @param direction
* @returns
*/
moveNode(node:INode, direction: 'up' | 'down'): void {
if (!node || !node.parent) {
return;
}
const parent = node.parent as GroupNode;
const currentIndex = parent.children.findIndex(child => child === node);
if (currentIndex === -1) {
return;
}
const newIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
// 範囲外のインデックスの処理
if (newIndex >= 0 && newIndex < parent.children.length) {
// ノードの位置を入れ替える
[parent.children[currentIndex], parent.children[newIndex]] = [parent.children[newIndex], parent.children[currentIndex]];
return; // 範囲外なら移動しない
}else if(newIndex<0 && parent.parent){
this.removeNode(node);
const parentIndex = (parent.parent as GroupNode).children.findIndex(child => child === parent);
(parent.parent as GroupNode).children.splice(parentIndex, 0, node);
node.parent = parent.parent;
}
}
getGroups(parent:GroupNode):number[]{
const groups:number[]=[];
groups.push(parent.index);
parent.children.forEach((node)=>{
if(node.type!==NodeType.Condition){
groups.push(...this.getGroups(node as GroupNode));
}
});
return groups;
}
/**
* 条件ノードをグループ化
* @param nodes 結合ノードを選択する
* @param logicOp
* @returns
*/
createGroupNode(firstNode:INode,nodes: INode[], logicOp: LogicalOperator): GroupNode | null {
if (nodes.length === 0) {
return null;
}
// 最初のノードの親ノードを取得
const parent = firstNode.parent as GroupNode;
if (!parent) {
throw new Error('ルートノードをグループ化できません');
}
// 親ノードを取得
const filteredNodes = nodes.filter(node => node.parent === parent);
// 新しいグループノードを作成
const newGroup = new GroupNode(logicOp, parent);
this.maxIndex++;
newGroup.index = this.maxIndex;
// 新しいグループノードの挿入位置を取得
let firstNodeIndex = parent.children.length;
if (filteredNodes.length > 0) {
firstNodeIndex = parent.children.indexOf(filteredNodes[0]);
}
filteredNodes.forEach(node => {
// 元の親ノードから削除する
const nodeIndex = parent.children.indexOf(node);
parent.children.splice(nodeIndex, 1);
// 新しいグループに追加
node.parent = newGroup;
newGroup.children.push(node);
});
// 新しいGroupNodeを挿入する
parent.children.splice(firstNodeIndex, 0, newGroup);
return newGroup;
}
/**
* GroupNodeを解散する
* @param groupNode 対象グループノード
*/
dissolveGroupNode(groupNode: GroupNode): void {
if (groupNode.parent === null || groupNode.type !== NodeType.LogicGroup) {
throw new Error('ルートノードと非グループノードを解散することができません');
}
// 親ノードを取得
const parent = groupNode.parent as GroupNode;
const groupIndex = parent.children.indexOf(groupNode);
// 子ノードをリセットする
groupNode.children.forEach(child => {
child.parent = parent;
parent.children.splice(groupIndex, 0, child);
});
//グループノードを削除する
parent.children.splice(groupIndex + groupNode.children.length, 1);
}
// Jsonから復元
fromJson(jsonString: string): INode {
const json = JSON.parse(jsonString);
this.root = GroupNode.fromJSON(json) as GroupNode;
this.maxIndex=this.getMaxIndex(this.root);
return this.root;
}
toJson():string{
return JSON.stringify(this.root, (key, value) => {
if (key === 'parent') {
return value ? value.type : null;
}
return value;
});
}
}

View File

@@ -1,112 +1,197 @@
import { publicDecrypt } from 'crypto';
import {IActionFlow} from './ActionTypes';
export interface TreeNode {
export interface IKintoneEventNode {
label: string;
header:string;
eventId:string;
parentId:string;
}
export interface IKintoneEvent extends TreeNode {
eventId: string;
export interface IKintoneEvent extends IKintoneEventNode {
hasFlow: boolean;
flowData?: IActionFlow;
}
export interface IKintoneScreen extends TreeNode {
label: string;
events: IKintoneEvent[];
export interface IKintoneEventGroup extends IKintoneEventNode {
events: IKintoneEventNode[];
}
export class kintoneEvent implements IKintoneEvent{
eventId: string;
parentId:string;
get hasFlow(): boolean{
return this.flowData!==undefined && this.flowData.actionNodes.length>1
};
flowData?: IActionFlow | undefined;
label: string;
constructor({eventId,label}:{eventId:string,label:string}){
get header():string{
return "EVENT";
}
constructor(label:string,eventId:string,parentId:string){
this.eventId=eventId;
this.label=label;
this.parentId=parentId;
}
}
export class kintoneEventGroup implements IKintoneEventGroup{
eventId: string;
parentId:string;
label: string;
events: IKintoneEventNode[];
get header():string{
return "EVENTGROUP";
}
constructor(eventId:string,label:string,events:IKintoneEventNode[],parentId:string){
this.eventId=eventId;
this.label=label;
this.events=events;
this.parentId=parentId;
}
}
export class kintoneEventForChange implements IKintoneEventGroup{
eventId: string;
parentId:string;
label: string;
events: IKintoneEventNode[];
get header():string{
return "CHANGE";
}
constructor(eventId:string,label:string,events:IKintoneEventNode[],parentId:string){
this.eventId=eventId;
this.label=label;
this.events=events;
this.parentId=parentId;
}
}
export class KintoneEventManager {
public screens: IKintoneScreen[];
public screens: IKintoneEventGroup[];
constructor(screens: IKintoneScreen[]) {
this.screens = screens;
constructor() {
this.screens = this.getKintoneEvents();
}
public bindFlows(flows:IActionFlow[]){
for (const screen of this.screens) {
screen.events.forEach((ev)=>ev.flowData=undefined);
}
this.screens=this.getKintoneEvents();
for (const flow of flows){
const eventId =flow.getRoot()?.name;
if(eventId!==undefined){
const event = this.findEventById(eventId);
if(event!==null){
const eventNode = this.findEventById(eventId);
if(eventNode!==null && eventNode.header==="EVENT"){
const event =eventNode as kintoneEvent;
event.flowData=flow;
}else{
//EventGroupのIDを取得
const lastIndex = eventId.lastIndexOf(".");
const groupId=eventId.substring(0,lastIndex);
const eventNode = this.findEventById(groupId);
if(eventNode && (eventNode.header==="EVENTGROUP" || eventNode.header==="CHANGE")){
const groupEvent=eventNode as kintoneEventGroup;
const newEvent =new kintoneEvent(
flow.getRoot()?.subTitle || "",
eventId,
groupEvent.parentId
);
newEvent.flowData=flow;
groupEvent.events.push(newEvent);
}
}
}
}
}
public findEventById(eventId: string): IKintoneEvent | null {
public findEventById(eventId: string): IKintoneEventNode | null {
const screen=this.findScreen(eventId);
if(screen) {return screen;}
for (const screen of this.screens) {
for (const event of screen.events) {
if (event.eventId === eventId) {
return event;
}
if(event.header==="EVENTGROUP"||event.header==="CHANGE"){
const eventGroup = event as IKintoneEventGroup;
const targetEvent = eventGroup.events.find((ev)=>{
return ev.eventId===eventId;
})
if(targetEvent){
return targetEvent;
}
}
}
}
return null;
}
public findScreen(eventId:string):IKintoneScreen|null{
for (const screen of this.screens) {
if(screen.events.some((ev:IKintoneEvent)=>ev.eventId===eventId)){
return screen;
public findScreen(eventId:string):IKintoneEventGroup|undefined{
return this.screens.find(screen=>screen.eventId==eventId);
}
}
return null;
public getKintoneEvents():IKintoneEventGroup[]{
return [
new kintoneEventGroup("app.record.create","レコード追加画面",[
new kintoneEvent('レコード追加画面を表示した後','app.record.create.show',"app.record.create"),
new kintoneEvent('保存をクリックしたとき','app.record.create.submit',"app.record.create"),
new kintoneEvent('保存が成功したとき','app.record.create.submit.success',"app.record.create"),
new kintoneEventForChange('app.record.create.change','フィールドの値を変更したとき',[],"app.record.create"),
new kintoneEventGroup('app.record.create.show.customButtonClick','ボタンをクリックした時',[],"app.record.create")
],""),
new kintoneEventGroup("app.record.detail","レコード詳細画面",[
new kintoneEvent('レコード詳細画面を表示した後','app.record.detail.show',"app.record.detail"),
new kintoneEvent('レコードを削除するとき','app.record.detail.delete.submit',"app.record.detail"),
new kintoneEvent('プロセス管理のアクションを実行したとき','app.record.detail.process.proceed',"app.record.detail"),
new kintoneEventGroup('app.record.detail.show.customButtonClick','ボタンをクリックした時',[],"app.record.detail"),
],""),
new kintoneEventGroup("app.record.edit","レコード編集画面",[
new kintoneEvent('レコード編集画面を表示した後','app.record.edit.show',"app.record.edit"),
new kintoneEvent('保存をクリックしたとき','app.record.edit.submit',"app.record.edit"),
new kintoneEvent('保存が成功したとき','app.record.edit.submit.success',"app.record.edit"),
new kintoneEventForChange('app.record.edit.change','フィールドの値を変更したとき',[],"app.record.edit"),
new kintoneEventGroup('app.record.edit.show.customButtonClick','ボタンをクリックした時',[],"app.record.edit"),
],""),
new kintoneEventGroup("app.record.index","レコード一覧画面",[
new kintoneEvent('一覧画面を表示した後', 'app.record.index.show',"app.record.index"),
new kintoneEvent('インライン編集を開始したとき','app.record.index.edit.show',"app.record.index"),
new kintoneEvent('インライン編集の【保存】をクリックしたとき','app.record.index.edit.submit',"app.record.index"),
new kintoneEvent('インライン編集の保存が成功したとき', 'app.record.index.edit.submit.success',"app.record.index"),
new kintoneEventForChange('app.record.index.edit.change','インライン編集のフィールド値を変更したとき' ,[],"app.record.index"),
new kintoneEventGroup('app.record.detail.show.customButtonClick','ボタンをクリックした時',[],"app.record.index"),
],"")
];
}
}
export const kintoneEvents:KintoneEventManager = new KintoneEventManager([
{
label:"レコード追加画面",
events:[
new kintoneEvent({label:"レコード追加画面を表示した後",eventId:"app.record.create.show"}),
new kintoneEvent({label:"保存をクリックしたとき",eventId:"app.record.create.submit"}),
new kintoneEvent({label:"保存が成功したとき",eventId:"app.record.create.submit.success"}),
new kintoneEvent({label:"フィールドの値を変更したとき",eventId:"app.record.create.change"}),
]
},
{
label:"レコード詳細画面",
events:[
new kintoneEvent({label:"レコード詳細画面を表示した後",eventId:"app.record.detail.show"}),
new kintoneEvent({label:"レコードを削除するとき",eventId:"app.record.detail.delete.submit"}),
new kintoneEvent({label:"プロセス管理のアクションを実行したとき",eventId:"app.record.detail.process.proceed"}),
]
},
{
label:"レコード編集画面",
events:[new kintoneEvent({label:"レコード編集画面を表示した後",eventId:"app.record.edit.show"}),
new kintoneEvent({label:"保存をクリックしたとき",eventId:"app.record.edit.submit"}),
new kintoneEvent({label:"保存が成功したとき",eventId:"app.record.edit.submit.success"}),
new kintoneEvent({label:"フィールドの値を変更したとき",eventId:"app.record.edit.change"}),
]
},
{
label:"レコード一覧画面",
events:[
new kintoneEvent({label:"一覧画面を表示した後", eventId:"app.record.index.show"}),
new kintoneEvent({label:"インライン編集を開始したとき",eventId:"app.record.index.edit.show"}),
new kintoneEvent({label:"インライン編集のフィールド値を変更したとき", eventId:"app.record.index.edit.change"}),
new kintoneEvent({label:"インライン編集の【保存】をクリックしたとき",eventId:"app.record.index.edit.submit"}),
new kintoneEvent({label:"インライン編集の保存が成功したとき", eventId:"app.record.index.edit.submit.success"}),
]
}
]);
// export const kintoneEvents:KintoneEventManager = new KintoneEventManager([
// new kintoneEventGroup("app.record.create","レコード追加画面",[
// new kintoneEvent('レコード追加画面を表示した後','app.record.create.show',"app.record.create"),
// new kintoneEvent('保存をクリックしたとき','app.record.create.submit',"app.record.create"),
// new kintoneEvent('保存が成功したとき','app.record.create.submit.success',"app.record.create"),
// new kintoneEventForChange('app.record.create.change','フィールドの値を変更したとき',[],"app.record.create"),
// new kintoneEventGroup('app.record.create.customButtonClick','ボタンをクリックした時',[],"app.record.create")
// ],""),
// new kintoneEventGroup("app.record.detail","レコード詳細画面",[
// new kintoneEvent('レコード詳細画面を表示した後','app.record.detail.show',"app.record.detail"),
// new kintoneEvent('レコードを削除するとき','app.record.detail.delete.submit',"app.record.detail"),
// new kintoneEvent('プロセス管理のアクションを実行したとき','app.record.detail.process.proceed',"app.record.detail"),
// new kintoneEventGroup('app.record.detail.customButtonClick','ボタンをクリックした時',[],"app.record.detail"),
// ],""),
// new kintoneEventGroup("app.record.edit","レコード編集画面",[
// new kintoneEvent('レコード編集画面を表示した後','app.record.edit.show',"app.record.edit"),
// new kintoneEvent('保存をクリックしたとき','app.record.edit.submit',"app.record.edit"),
// new kintoneEvent('保存が成功したとき','app.record.edit.submit.success',"app.record.edit"),
// new kintoneEventForChange('app.record.edit.change','フィールドの値を変更したとき',[],"app.record.edit"),
// new kintoneEventGroup('app.record.edit.customButtonClick','ボタンをクリックした時',[],"app.record.edit"),
// ],""),
// new kintoneEventGroup("app.record.index","レコード一覧画面",[
// new kintoneEvent('一覧画面を表示した後', 'app.record.index.show',"app.record.index"),
// new kintoneEvent('インライン編集を開始したとき','app.record.index.edit.show',"app.record.index"),
// new kintoneEvent('インライン編集の【保存】をクリックしたとき','app.record.index.edit.submit',"app.record.index"),
// new kintoneEvent('インライン編集の保存が成功したとき', 'app.record.index.edit.submit.success',"app.record.index"),
// new kintoneEventForChange('app.record.index.edit.change','インライン編集のフィールド値を変更したとき' ,[],"app.record.index"),
// new kintoneEventGroup('app.record.detail.customButtonClick','ボタンをクリックした時',[],"app.record.index"),
// ],"")
// ]);

View File

@@ -76,7 +76,7 @@
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
@@ -299,7 +299,7 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.10.0":
"@typescript-eslint/parser@^5.10.0":
version "5.61.0"
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.61.0.tgz"
integrity sha512-yGr4Sgyh8uO6fSi9hw3jAFXNBHbCtKKFMdX2IkT3ZqpKmtAq3lHS4ixB/COFuAIJpwl9/AqF7j72ZDWYKmIfvg==
@@ -367,7 +367,7 @@
"@typescript-eslint/types" "5.61.0"
eslint-visitor-keys "^3.3.0"
"@vitejs/plugin-vue@^2.0.0 || ^3.0.0 || ^4.0.0", "@vitejs/plugin-vue@^2.2.0":
"@vitejs/plugin-vue@^2.2.0":
version "2.3.4"
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz"
integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg==
@@ -480,22 +480,12 @@ acorn-jsx@^5.3.2:
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.9.0:
acorn@^8.9.0:
version "8.10.0"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz"
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
ajv@^6.10.0:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^6.12.4:
ajv@^6.10.0, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -691,7 +681,7 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
browserslist@^4.21.5, "browserslist@>= 4.21.0":
browserslist@^4.21.5:
version "4.21.9"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz"
integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==
@@ -763,7 +753,7 @@ chardet@^0.7.0:
resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
chokidar@^3.5.3, "chokidar@>=3.0.0 <4.0.0":
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
@@ -948,27 +938,6 @@ csstype@^3.1.1:
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
debug@^4.1.1:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@^4.3.2:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
@@ -976,6 +945,13 @@ debug@2.6.9:
dependencies:
ms "2.0.0"
debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
deep-is@^0.1.3:
version "0.1.4"
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
@@ -1068,12 +1044,107 @@ end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
esbuild-android-64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz#414a087cb0de8db1e347ecca6c8320513de433db"
integrity sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==
esbuild-android-arm64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz#55de3bce2aab72bcd2b606da4318ad00fb9c8151"
integrity sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==
esbuild-darwin-64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz#4259f23ed6b4cea2ec8a28d87b7fb9801f093754"
integrity sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==
esbuild-darwin-arm64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz#d77b4366a71d84e530ba019d540b538b295d494a"
integrity sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==
esbuild-freebsd-64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz#27b6587b3639f10519c65e07219d249b01f2ad38"
integrity sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==
esbuild-freebsd-arm64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz#63c435917e566808c71fafddc600aca4d78be1ec"
integrity sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==
esbuild-linux-32@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz#c3da774143a37e7f11559b9369d98f11f997a5d9"
integrity sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==
esbuild-linux-64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz#5d92b67f674e02ae0b4a9de9a757ba482115c4ae"
integrity sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==
esbuild-linux-arm64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz#dac84740516e859d8b14e1ecc478dd5241b10c93"
integrity sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==
esbuild-linux-arm@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz#b3ae7000696cd53ed95b2b458554ff543a60e106"
integrity sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==
esbuild-linux-mips64le@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz#dad10770fac94efa092b5a0643821c955a9dd385"
integrity sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==
esbuild-linux-ppc64le@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz#b68c2f8294d012a16a88073d67e976edd4850ae0"
integrity sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==
esbuild-linux-riscv64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz#608a318b8697123e44c1e185cdf6708e3df50b93"
integrity sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==
esbuild-linux-s390x@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz#c9e7791170a3295dba79b93aa452beb9838a8625"
integrity sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==
esbuild-netbsd-64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz#0abd40b8c2e37fda6f5cc41a04cb2b690823d891"
integrity sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==
esbuild-openbsd-64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz#4adba0b7ea7eb1428bb00d8e94c199a949b130e8"
integrity sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==
esbuild-sunos-64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz#4b8a6d97dfedda30a6e39607393c5c90ebf63891"
integrity sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==
esbuild-windows-32@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz#d31d8ca0c1d314fb1edea163685a423b62e9ac17"
integrity sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==
esbuild-windows-64@0.14.51:
version "0.14.51"
resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz"
integrity sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==
esbuild@^0.14.27, esbuild@0.14.51:
esbuild-windows-arm64@0.14.51:
version "0.14.51"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz#0220d2304bfdc11bc27e19b2aaf56edf183e4ae9"
integrity sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==
esbuild@0.14.51, esbuild@^0.14.27:
version "0.14.51"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.51.tgz"
integrity sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==
@@ -1145,15 +1216,7 @@ eslint-scope@^5.1.1:
esrecurse "^4.3.0"
estraverse "^4.1.1"
eslint-scope@^7.1.1:
version "7.2.0"
resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz"
integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==
dependencies:
esrecurse "^4.3.0"
estraverse "^5.2.0"
eslint-scope@^7.2.0:
eslint-scope@^7.1.1, eslint-scope@^7.2.0:
version "7.2.0"
resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz"
integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==
@@ -1166,7 +1229,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1:
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz"
integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
eslint@*, "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0", eslint@^8.10.0, eslint@^8.11.0, eslint@>=6.0.0, eslint@>=7.0.0:
eslint@^8.10.0:
version "8.44.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz"
integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==
@@ -1239,12 +1302,7 @@ estraverse@^4.1.1:
resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
estraverse@^5.1.0:
version "5.3.0"
resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
estraverse@^5.2.0:
estraverse@^5.1.0, estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
@@ -1315,7 +1373,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^3.2.9, fast-glob@3.2.12:
fast-glob@3.2.12, fast-glob@^3.2.9:
version "3.2.12"
resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz"
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
@@ -1446,6 +1504,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
@@ -1572,7 +1635,7 @@ http-errors@2.0.0:
statuses "2.0.1"
toidentifier "1.0.1"
iconv-lite@^0.4.24, iconv-lite@0.4.24:
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -1610,7 +1673,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@2, inherits@2.0.4:
inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -1894,7 +1957,7 @@ micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
"mime-db@>= 1.43.0 < 2", mime-db@1.52.0:
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
@@ -2149,7 +2212,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pinia@^2.0.0, pinia@^2.1.6:
pinia@^2.1.6:
version "2.1.6"
resolved "https://registry.npmjs.org/pinia/-/pinia-2.1.6.tgz"
integrity sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==
@@ -2170,7 +2233,7 @@ postcss-value-parser@^4.2.0:
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.1.0, postcss@^8.1.10, postcss@^8.4.13:
postcss@^8.1.10, postcss@^8.4.13:
version "8.4.25"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz"
integrity sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==
@@ -2219,7 +2282,7 @@ qs@6.11.0:
dependencies:
side-channel "^1.0.4"
quasar@^2.6.0, quasar@^2.8.0:
quasar@^2.6.0:
version "2.12.2"
resolved "https://registry.npmjs.org/quasar/-/quasar-2.12.2.tgz"
integrity sha512-UB+J1cN6b9FrRphUuMvW1Pt1uaOPH2XJMAZagQlYzVUmltKCXQ0sby+ANgGRYa8w/tsekMySmN9QvkQ3+hYv/g==
@@ -2251,20 +2314,7 @@ raw-body@2.5.1:
iconv-lite "0.4.24"
unpipe "1.0.0"
readable-stream@^2.0.0:
version "2.3.8"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^2.0.5:
readable-stream@^2.0.0, readable-stream@^2.0.5:
version "2.3.8"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
@@ -2364,7 +2414,7 @@ rollup-plugin-visualizer@^5.5.4:
source-map "^0.7.4"
yargs "^17.5.1"
"rollup@>=2.59.0 <2.78.0", "rollup@2.x || 3.x":
"rollup@>=2.59.0 <2.78.0":
version "2.77.3"
resolved "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz"
integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
@@ -2390,7 +2440,7 @@ rxjs@^7.5.5:
dependencies:
tslib "^2.1.0"
safe-buffer@^5.1.0, safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@5.1.2:
safe-buffer@5.1.2, safe-buffer@^5.1.0, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
@@ -2405,7 +2455,7 @@ safe-buffer@5.2.1:
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sass@*, sass@1.32.12:
sass@1.32.12:
version "1.32.12"
resolved "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz"
integrity sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==
@@ -2537,13 +2587,6 @@ statuses@2.0.1:
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
string_decoder@^1.1.1, string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
@@ -2553,6 +2596,13 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string_decoder@^1.1.1, string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
@@ -2670,7 +2720,7 @@ type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"
typescript@^4.5.4, "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=4.4.4:
typescript@^4.5.4:
version "4.9.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz"
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
@@ -2685,7 +2735,7 @@ universalify@^2.0.0:
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unpipe@~1.0.0, unpipe@1.0.0:
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
@@ -2730,7 +2780,7 @@ vary@~1.1.2:
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
"vite@^2.0.0 || ^3.0.0 || ^4.0.0", vite@^2.5.10, vite@^2.9.13:
vite@^2.9.13:
version "2.9.16"
resolved "https://registry.npmjs.org/vite/-/vite-2.9.16.tgz"
integrity sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==
@@ -2760,14 +2810,14 @@ vue-eslint-parser@^9.3.0:
lodash "^4.17.21"
semver "^7.3.6"
vue-router@^4.0.0, vue-router@^4.0.12:
vue-router@^4.0.0:
version "4.2.4"
resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.2.4.tgz"
integrity sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==
dependencies:
"@vue/devtools-api" "^6.5.0"
"vue@^2.6.14 || ^3.3.0", vue@^3.0.0, "vue@^3.0.0-0 || ^2.6.0", vue@^3.2.0, vue@^3.2.25, vue@^3.2.29, vue@3.3.4:
vue@^3.0.0:
version "3.3.4"
resolved "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz"
integrity sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==

24
plugin/kintone-addins/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

800
plugin/kintone-addins/package-lock.json generated Normal file
View File

@@ -0,0 +1,800 @@
{
"name": "kintone-addins",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "kintone-addins",
"version": "0.0.0",
"dependencies": {
"jquery": "^3.7.1"
},
"devDependencies": {
"@types/jquery": "^3.5.24",
"@types/node": "^20.8.9",
"sass": "^1.69.5",
"typescript": "^5.0.2",
"vite": "^4.4.5"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@types/jquery": {
"version": "3.5.24",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.24.tgz",
"integrity": "sha512-V/TG69ge5amcr8Ap7vY3SObqKfZlV7ttqcYnNcYnndI77ySIRi05+3GjvfwRtE2qalAC2ySLIL1ker512sI20g==",
"dev": true,
"dependencies": {
"@types/sizzle": "*"
}
},
"node_modules/@types/node": {
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz",
"integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/sizzle": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.5.tgz",
"integrity": "sha512-tAe4Q+OLFOA/AMD+0lq8ovp8t3ysxAOeaScnfNdZpUxaGl51ZMDEITxkvFl1STudQ58mz6gzVGl9VhMKhwRnZQ==",
"dev": true
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/esbuild": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.18.20",
"@esbuild/android-arm64": "0.18.20",
"@esbuild/android-x64": "0.18.20",
"@esbuild/darwin-arm64": "0.18.20",
"@esbuild/darwin-x64": "0.18.20",
"@esbuild/freebsd-arm64": "0.18.20",
"@esbuild/freebsd-x64": "0.18.20",
"@esbuild/linux-arm": "0.18.20",
"@esbuild/linux-arm64": "0.18.20",
"@esbuild/linux-ia32": "0.18.20",
"@esbuild/linux-loong64": "0.18.20",
"@esbuild/linux-mips64el": "0.18.20",
"@esbuild/linux-ppc64": "0.18.20",
"@esbuild/linux-riscv64": "0.18.20",
"@esbuild/linux-s390x": "0.18.20",
"@esbuild/linux-x64": "0.18.20",
"@esbuild/netbsd-x64": "0.18.20",
"@esbuild/openbsd-x64": "0.18.20",
"@esbuild/sunos-x64": "0.18.20",
"@esbuild/win32-arm64": "0.18.20",
"@esbuild/win32-ia32": "0.18.20",
"@esbuild/win32-x64": "0.18.20"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/immutable": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
"integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
"dev": true
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/jquery": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/rollup": {
"version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/sass": {
"version": "1.69.7",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz",
"integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/vite": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
"dev": true,
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.27",
"rollup": "^3.27.1"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"@types/node": ">= 14",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
{
"name": "kintone-addins",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "tsc && set \"SOURCE_MAP=true\" && vite build && vite preview",
"build": "tsc && vite build && xcopy dist\\*.js ..\\..\\backend\\Temp\\ /E /I /Y",
"build:dev":"tsc && set \"SOURCE_MAP=true\" && vite build && xcopy dist\\*.js ..\\..\\backend\\Temp\\ /E /I /Y",
"preview": "vite preview",
"ngrok":"ngrok http http://localhost:4173/",
"vite":"vite dev"
},
"devDependencies": {
"@types/jquery": "^3.5.24",
"@types/node": "^20.8.9",
"sass": "^1.69.5",
"typescript": "^5.0.2",
"vite": "^4.4.5"
},
"dependencies": {
"jquery": "^3.7.1"
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,295 @@
# kintone自動化開発ツールのアクションのアドイン開発手順
## 1. アクションの登録
アクションプラグインをシステムに登録するためには、以下の情報をデータベースの`action`表に挿入する必要があります。
|列名 | 項目 | 説明 |
|----- |-------------|-------------------------------------------|
|name | 名前 | アクションプラグイン名(ユニークな名前が必要) |
|title |タイトル | タイトル 20文字以内 |
|subtitle|サブタイトル | サブタイトル |
|outputpoint|出力ポイント | 出力値に分岐がある場合の接続点 |
|property|プロパティ | アクションプラグインの属性json形式 |
### 登録の例
以下は「表示/非表示」アクションプラグインを登録する例です。
- name: "表示/非表示"
- title: "指定項目の表示・非表示を設定する"
- subtitle: "表示/非表示"
- outputpoint: "[]"
- property:
```json
[
{
"component": "FieldInput",
"props": {
"displayName": "フィールド",
"modelValue": {},
"name": "field",
"placeholder": "対象項目を選択してください"
}
},
{
"component": "SelectBox",
"props": {
"displayName": "表示/非表示",
"options": ["表示", "非表示"],
"modelValue": "",
"name": "show",
"placeholder": ""
}
},
{
"component": "ConditionInput",
"props": {
"displayName": "条件",
"modelValue": "",
"name": "condition",
"placeholder": "条件式を設定してください"
}
}
]
```
### プロパティ属性設定画面
![プロパティ属性設定画面](../../document/action-property.png)
### 属性UIコンポーネントの共通属性
| 属性 | 設定値の例 | 説明 |
|-------------|--------------------|----------------------------------------------------------------------------|
| component | InputText | コンポーネントの種類を示しており、この場合は選択リストを意味します。<br>使用可能なコンポーネントを参照|
| displayName | 表示/非表示 | ユーザーに対して表示されるコンポーネントの名前です。 |
| options | ["表示", "非表示"] | ユーザーが選択できるオプションの配列です。<br>SelectBoxのみ使用可能 |
| modelValue | 空文字 | コンポーネントの初期値を設定します。<br>初期設定ないの場合は空文字で設定する。
| name | field | 属性の設定値の名前です。 |
| placeholder | 対象項目を選択してください| 入力フィールドに表示されるプレースホルダーのテキストです。この場合は設定されていません。 |
| hint | 説明文| 長い説明文を設定することが可能です。markdown形式サポート予定、現在HTML可能 |
| selectType |`single` or `multiple`| フィールド選択・他のアプリのフィールド選択の選択モードを設定する |
### 使用可能なコンポーネント
| No. | コンポーネント名 | コンポーネントタイプ | 説明 |
|-----|------------------|------------------|-----------------------------------------|
| 1 | テキストボックス | InputText | 一行のテキスト入力が可能なフィールドです。 |
| 2 | テキストボックス(改行可能) | MuiltInputText | 複数行のテキスト入力が可能なテキストエリアです。 |
| 3 | 日付 | DatePicker | 日付を選択するためのカレンダーコンポーネントです。 |
| 4 | フィールド選択 | FieldInput | システムのフィールドを選択するための入力コンポーネントです。 |
| 5 | 選択リスト | SelectBox | 複数のオプションから選択するためのドロップダウンリストです。 |
| 6 | 条件式設定 | ConditionInput | 条件式やロジックを入力するためのコンポーネントです。 |
| 7 | イベント設定 |EventSetter | ボタンイベント設定のコンポーネントです。 |
| 8 | 色選択 | ColorPicker | 色を設定する(追加予定中) |
| 9 | 他のアプリのフィールド選択 | AppFieldPicker | 他のアプリのフィールドを選択する(追加予定中) |
| 10 |ユーザー選択 | UserPicker | ユーザーを選択する(追加予定中) |
## 2.アクションアドインの開発
### 1. Action pluginファイルの追加
アクションプラグインを作成するためには、以下のディレクトリ構造に`TypeScript`ファイルを追加します。
```
KintoneAppBuilder
└─ plugin
└─ kintone-addins
└─ src
└─ actions
└─ your-action.ts // ここにアクションプラグインのtsファイルを追加
```
### 2. アクションクラスの実装手順
`IAction` インターフェースに従ってアクションクラスを実装します。
```typescript
/**
* アクションのインターフェース
*/
export interface IAction{
// アクションのユニークな名前
name:string;
//属性設定情報
actionProps: Array<IActionProperty>;
//アクションのプロセス実行関数
process(prop:IActionNode,event:any,context:IContext):Promise<IActionResult>;
//アクションの登録関数
register():void;
}
```
#### サンプルコード
```ts
// アクションの属性定義
interface IShownProps{
field:IField;
show:string;
condition:string;
}
// 表示/非表示アクション
export class FieldShownAction implements IAction{
name: string;
actionProps: IActionProperty[];
props:IShownProps;
constructor(){
this.name="表示/非表示"; // DBに登録したアクション名一致する必要があり
this.actionProps=[];
//プロパティ属性の初期化
this.props={
field:{code:''},
show:'',
condition:''
}
//アクションの自動登録
this.register();
}
/**
* アクションの実行を呼び出す
* @param actionNode
* @param event
* @returns
*/
async process(actionNode:IActionNode,event:any,context:IContext):Promise<IActionResult> {
// ... (アクション処理の実装)
}
register(): void {
actionAddins[this.name]=this;
}
}
new FieldShownAction();
```
アクションプラグインを実装するには、`IAction`インターフェースの定義に従って、必要なメソッドとプロパティをクラスに実装します。
以下に、`IAction`インターフェースを用いて`表示/非表示`アクションを実装する手順を説明します。
1. **アクションの属性定義**
2. **アクションクラスの作成**:
- `IAction`インターフェースを実装する新しいクラス`FieldShownAction`を作成します。
3. **コンストラクタの定義**:
- アクション名や初期プロパティを設定します。
- このクラスのインスタンスが作成された際に、自動的にアクションが登録されるように、コンストラクタ内で`register`メソッドを呼び出します。
4. **プロセス実行関数の実装** (`process`):
- `process`メソッドは、アクションの主要なロジックを含み、アクションの実行時に呼び出されます。
- * 以下は`process`関数のパラメータとその用途を説明します。
| パラメータ名 | 型 | 用途 |
|----------|----------------|------------------------------------------------------------------------------------------------|
| actionNode | `IActionNode` | Kintone自動化ツールのアクションの設定やプロパティ情報を保持します。 |
| event |kintoneのイベント情報| レコードやエラー制御で使用します |
| context | `IContext` | 現在のレコード情報や変数など、実行に必要なデータへのアクセスを提供します。 |
- このメソッド内で、アクションに必要な処理を行います。
- 1. アクションプロパティの取得:
`Kitone自動化ツール`を設定したプロパティの値を取得する
```ts
//プロパティ設定を取得する
this.actionProps=actionNode.actionProps;
//プロパティ設定のデータ型は必要な情報が含めますか
if (!('field' in actionNode.ActionValue) && !('show' in actionNode.ActionValue)) {
return result
}
//既定のプロパティのインターフェースへ変換する
this.props = actionNode.ActionValue as IShownProps;
```
- 2. 条件式の評価
getConditionResult関数を呼び出して条件式を評価します。この関数は、現在のコンテキストに基づいて条件式が真か偽かを返します。
```ts
//条件式の計算結果を取得
const conditionResult = this.getConditionResult(context);
/**
*
* @param context 条件式を実行する
* @returns
*/
getConditionResult(context:any):boolean{
//プロパティ`condition`から条件ツリーを取得する
const tree =this.getCondition(this.props.condition);
if(!tree){
//条件を設定されていません
return true;
}
return tree.evaluate(tree.root,context);
}
```
- 3. Kintone APIを使用して、フィールドの表示非表示の制御
```ts
//条件式の計算結果を取得
const conditionResult = this.getConditionResult(context);
if(conditionResult){
if(this.props.show==='表示'){
kintone.app.record.setFieldShown(this.props.field.code,true);
}else if (this.props.show==='非表示'){
kintone.app.record.setFieldShown(this.props.field.code,false);
}
}
```
5. **登録関数の実装** (`register`):
- アクションをアドインシステムに登録するための`register`メソッドを実装します。
6. **アクションプロセス`ActionProcess`に参照追加**
```ts
import { actionAddins } from "../actions";
import '../actions/must-input';
import '../actions/auto-numbering';
import '../actions/field-shown';
import '../actions/your-action'; //ここに新規のアクションの参照を追加する
...
```
### 3. デプロイ
1. **プロジェクトをビルドする**
- 本番環境にデプロイする場合
```bash
cd plug\kintone-addins\
npm install
npm run build
```
- 開発環境にデプロイする場合(ソースマップ出力ます)
```bash
cd plug\kintone-addins\
npm install
npm run build:dev
```
2. **Azureにデプロイする**
- Azure 拡張機能のインストール:
VSCode の拡張機能ペインで`Azure Tools`を検索し、インストールします。
- Azure にログイン:
- Azure Account 拡張機能を使用して Azure にログインします。
- Azure へのデプロイ:
- 「Deploy to Web App」オプションを使用し、デプロイするファイルやフォルダを指定します。
- デプロイの確認:
- Azure App Service 拡張機能でデプロイが完了したことを確認します。
- ka-addin の URL にアクセスしてアプリケーションが正常に動作しているか確認します。
3. **ローカルでプラグインをテストする**
1. kintone-addinsをPreviewで起動する
```bash
yarn build:dev
yarn preview
#またはyarn devは yarn build:dev + yarn preview と同じです
yarn dev
```
2. **ngrokをインストールする**
1. [ngrok の公式ウェブサイト](https://ngrok.com/)にアクセスします。
2. 「Sign up」をクリックしてアカウントを登録するか、既存のアカウントにログインします。
3. 登録またはログイン後、ダッシュボードに進み、ダウンロードリンクが表示されます。操作システムWindows、macOS、Linuxに応じて、適切なバージョンを選択してダウンロードします。
4. ダウンロード後、`.zip` ファイルを解凍します。
5. ngrok を設定する
1. ngrok ダッシュボードにログインし、ホームページで認証トークンを見つけます。
2. ターミナル(またはコマンドプロンプト)を開き、以下のコマンドを実行して認証トークンを追加します:
```bash
ngrok config add-authtoken <認証トークン>
```
6. ngrok を起動する
```bash
ngrok https http://localhost:4173/
```

View File

@@ -0,0 +1,147 @@
import { actionAddins } from ".";
import { IField, IAction,IActionResult, IActionNode, IActionProperty, IContext } from "../types/ActionTypes";
import { Formatter } from "../util/format";
declare global {
interface Window { $format: any; }
}
interface IAutoNumberingProps{
//文書番号を格納する
field:IField;
format:string;
prefix:string;
suffix:string;
verName:string;
}
export class AutoNumbering implements IAction{
name: string;
actionProps: IActionProperty[];
props:IAutoNumberingProps;
constructor(){
this.name="自動採番する";
this.actionProps=[];
this.register();
this.props={
field:{code:''},
format:'',
prefix:'',
suffix:'',
verName:''
}
globalThis.window.$format=this.format;
this.register();
}
/**
* アクションの処理を実装する
* @param actionNode アクションノード
* @param event Kintoneのイベント
* @param context コンテキスト(レコード、変数情報を持っている)
* @returns
*/
async process(actionNode:IActionNode,event:any,context:IContext):Promise<IActionResult> {
let result={
canNext:false,
result:false
};
try{
this.actionProps=actionNode.actionProps;
if (!('field' in actionNode.ActionValue) && !('format' in actionNode.ActionValue)) {
return result
}
this.props = actionNode.ActionValue as IAutoNumberingProps;
const record = event.record;
const docNum = await this.createNumber(this.props);
record[this.props.field.code].value=docNum;
//変数設定
if(this.props.verName){
context.variables[this.props.verName]=docNum;
}
result= {
canNext:true,
result:true
}
return result;
}catch(error){
console.error(error);
event.error="処理中異常が発生しました。";
return {
canNext:false,
result:false
}
}
}
execTemplate(template:string):string{
if(template===undefined) return '';
const regex = /\$\{([^}]+)\}/g;
return template.replace(regex,(match,expr)=>{
return this.execEval(match,expr);
});
}
execEval(match:string,expr:string):string{
console.log(match);
return eval(expr);
}
format(pattern:string):string{
const now=new Date();
return Formatter.dateFormat(now, pattern);
}
async createNumber(props:IAutoNumberingProps){
let number :string='';
let prefix:string='';
let suffix:string='';
let no=1;
try{
no = await this.fetchNo();
}catch(error){
console.log(error);
}
if(props.format!==undefined && props.format!==''){
number=Formatter.numberFormat(no, props.format);
}else{
number=no.toString(10);
}
if(props.prefix!==undefined && props.prefix!==''){
prefix=this.execTemplate(props.prefix);
}
if(props.suffix!==undefined && props.suffix!==''){
suffix=this.execTemplate(props.suffix);
}
return `${prefix}${number}${suffix}`;
}
async fetchNo():Promise<number>{
let recNo=1;
return await new kintone.Promise<number>((resolve,reject)=>{
const appurl = kintone.api.url('/k/v1/records',true);
const params={
app:kintone.app.getId(),
fields:['$id'],
query:'limit 1'
};
return kintone.api(appurl,'GET',params).then((resp)=>{
if(resp.records[0]!==null){
recNo = parseInt(resp.records[0].$id.value,10)+1;
}
resolve(recNo);
}).catch((error)=>{
reject(error);
});
});
}
register(): void {
actionAddins[this.name]=this;
}
}
new AutoNumbering();

View File

@@ -0,0 +1,107 @@
import { actionAddins } from ".";
import $ from 'jquery';
import { IAction, IActionProperty, IActionNode, IActionResult } from "../types/ActionTypes";
/**
* ボタン配置属性定義
*/
interface IButtonAddProps {
//ボタン表示名
buttonName: string;
//配置位置
position: string;
//イベント名
eventName:string
}
export class ButtonAddAction implements IAction {
name: string;
actionProps: IActionProperty[];
props: IButtonAddProps;
constructor() {
this.name = "ボタンの配置";
this.actionProps = [];
this.props = {
buttonName: '',
position: '',
eventName:''
}
this.register();
}
/**
* アクションの実行を呼び出す
* @param actionNode
* @param event
* @returns
*/
async process(actionNode: IActionNode, event: any): Promise<IActionResult> {
let result = {
canNext: true,
result: false
};
try {
this.actionProps = actionNode.actionProps;
if (!('buttonName' in actionNode.ActionValue) && !('position' in actionNode.ActionValue)) {
return result
}
this.props = actionNode.ActionValue as IButtonAddProps;
//ボタンを配置する
const menuSpace = kintone.app.record.getHeaderMenuSpaceElement();
if(!menuSpace) return result;
if($("style#alc-button-add").length===0){
const css=`
.alc-button-normal {
display: inline-block;
box-sizing: border-box;
padding: 0 16px;
margin-left: 16px;
margin-top: 8px;
min-width: 100px;
outline: none;
border: 1px solid #e3e7e8;
background-color: #f7f9fa;
box-shadow: 1px 1px 1px #fff inset;
color: #3498db;
text-align: center;
line-height: 32px;
}
.alc-button-normal:hover {
background-color: #c8d6dd;
box-shadow: none;
cursor: pointer;
}
.alc-button-normal:active {
color: #f7f9fa;
background-color: #54b8eb;
}`;
const style = $("<style id='alc-button-add'>/<style>");
style.text(css);
$("head").append(style);
}
const button =$(`<button id='${this.props.eventName}' class='alc-button-normal' >${this.props.buttonName}</button>`);
if(this.props.position==="一番左に追加する"){
$(menuSpace).prepend(button);
}else{
$(menuSpace).append(button);
}
const clickEventName = `${event.type}.customButtonClick.${this.props.eventName}`;
button.on("click",()=>{
$(document).trigger(clickEventName,event);
});
return result;
} catch (error) {
event.error = error;
console.error(error);
result.canNext = false;
return result;
}
}
register(): void {
actionAddins[this.name] = this;
}
}
new ButtonAddAction();

View File

@@ -0,0 +1,109 @@
import { actionAddins } from ".";
import { IAction,IActionResult, IActionNode, IActionProperty, IContext } from "../types/ActionTypes";
import { ConditionTree } from '../types/Conditions';
/**
* アクションの属性定義
*/
interface ICondition{
condition:string;
verName:string;
}
/**
* 条件分岐アクション
*/
export class ConditionAction implements IAction{
name: string;
actionProps: IActionProperty[];
props:ICondition;
constructor(){
this.name="条件式";
this.actionProps=[];
this.props={
condition:'',
verName:''
}
//アクションを登録する
this.register();
}
/**
* アクションの実行を呼び出す
* @param actionNode
* @param event
* @returns
*/
async process(actionNode:IActionNode,event:any,context:IContext):Promise<IActionResult> {
let result={
canNext:true,
result:''
};
try{
//属性設定を取得する
this.actionProps=actionNode.actionProps;
if (!('condition' in actionNode.ActionValue) && !('verName' in actionNode.ActionValue)) {
return result
}
this.props = actionNode.ActionValue as ICondition;
//条件式の計算結果を取得
const conditionResult = this.getConditionResult(context);
console.log("条件計算結果:",conditionResult);
if(conditionResult){
result= {
canNext:true,
result:'はい'
}
}else{
result= {
canNext:true,
result:'いいえ'
}
}
if(this.props.verName){
context.variables[this.props.verName]=result.result;
}
return result;
}catch(error){
event.error=error;
console.error(error);
result.canNext=false;
return result;
}
}
/**
*
* @param context 条件式を実行する
* @returns
*/
getConditionResult(context:any):boolean{
const tree =this.getCondition(this.props.condition);
if(!tree){
//条件を設定されていません
return true;
}
return tree.evaluate(tree.root,context);
}
/**
* @param condition 条件式ツリーを取得する
* @returns
*/
getCondition(condition:string):ConditionTree|null{
try{
const tree = new ConditionTree();
tree.fromJson(condition);
if(tree.getConditions(tree.root).length>0){
return tree;
}else{
return null;
}
}catch(error){
return null;
}
}
register(): void {
actionAddins[this.name]=this;
}
}
new ConditionAction();

View File

@@ -0,0 +1,99 @@
import { actionAddins } from ".";
import { IAction, IActionProperty, IActionNode, IContext, IActionResult } from "../types/ActionTypes";
import { ConditionTree } from '../types/Conditions';
/**
* アクションの属性定義
*/
interface IErrorShowProps {
message: string;
condition: string;
}
export class ErrorShowAction implements IAction {
name: string;
actionProps: IActionProperty[]; //调用从import导入关于显示类的定义
props: IErrorShowProps;//从上方的interface 定义这个props所需要接受的属性
constructor() { //解构函数定义需要的类名
this.name = "エラー表示";
this.actionProps = [];
this.props = {
message: '',
condition: ''
}
this.register(); //重置以上注册表
}
/**
* アクションの実行を呼び出す
* @param actionNode
* @param event
* @returns
*/
async process(actionNode: IActionNode, event: any, context: IContext): Promise<IActionResult> { //异步处理某函数下的xx属性
let result = {
canNext: true,
result: false
};
try { //尝试执行以下代码部分
this.actionProps = actionNode.actionProps;
if (!('message' in actionNode.ActionValue) && !('condition' in actionNode.ActionValue)) { //如果message以及condition两者都不存在的情况下return
return result
}
this.props = actionNode.ActionValue as IErrorShowProps;
const conditionResult = this.getConditionResult(context);
if (conditionResult) {
event.error = this.props.message;
} else {
result = {
canNext: false,
result: true
}
}
return result;
} catch (error) {
event.error = error;
console.error(error);
result.canNext = false;
return result;
}
}
/**
*
* @param context 条件式を実行する
* @returns
*/
getConditionResult(context: any): boolean {
const tree = this.getCondition(this.props.condition);
if (!tree) {
//条件を設定されていません
return true;
}
return tree.evaluate(tree.root, context);
}
/**
* @param condition 条件式ツリーを取得する
* @returns
*/
getCondition(condition: string): ConditionTree | null {
try {
const tree = new ConditionTree();
tree.fromJson(condition);
if (tree.getConditions(tree.root).length > 0) {
return tree;
} else {
return null;
}
} catch (error) {
return null;
}
}
register(): void {
actionAddins[this.name] = this;
}
}
new ErrorShowAction();

View File

@@ -0,0 +1,108 @@
import { actionAddins } from ".";
import { IAction,IActionResult, IActionNode, IActionProperty, IField, IContext } from "../types/ActionTypes";
import { ConditionTree } from '../types/Conditions';
/**
* アクションの属性定義
*/
interface IShownProps{
field:IField;
show:string;
condition:string;
}
/**
* 表示/非表示アクション
*/
export class FieldShownAction implements IAction{
name: string;
actionProps: IActionProperty[];
props:IShownProps;
constructor(){
this.name="表示/非表示";
this.actionProps=[];
this.props={
field:{code:''},
show:'',
condition:''
}
//アクションを登録する
this.register();
}
/**
* アクションの実行を呼び出す
* @param actionNode
* @param event
* @returns
*/
async process(actionNode:IActionNode,event:any,context:IContext):Promise<IActionResult> {
let result={
canNext:true,
result:false
};
try{
//属性設定を取得する
this.actionProps=actionNode.actionProps;
if (!('field' in actionNode.ActionValue) && !('show' in actionNode.ActionValue)) {
return result
}
this.props = actionNode.ActionValue as IShownProps;
//条件式の計算結果を取得
const conditionResult = this.getConditionResult(context);
if(conditionResult){
if(this.props.show==='表示'){
kintone.app.record.setFieldShown(this.props.field.code,true);
}else if (this.props.show==='非表示'){
kintone.app.record.setFieldShown(this.props.field.code,false);
}
}
result= {
canNext:true,
result:true
}
return result;
}catch(error){
event.error=error;
console.error(error);
result.canNext=false;
return result;
}
}
/**
*
* @param context 条件式を実行する
* @returns
*/
getConditionResult(context:any):boolean{
const tree =this.getCondition(this.props.condition);
if(!tree){
//条件を設定されていません
return true;
}
return tree.evaluate(tree.root,context);
}
/**
* @param condition 条件式ツリーを取得する
* @returns
*/
getCondition(condition:string):ConditionTree|null{
try{
const tree = new ConditionTree();
tree.fromJson(condition);
if(tree.getConditions(tree.root).length>0){
return tree;
}else{
return null;
}
}catch(error){
return null;
}
}
register(): void {
actionAddins[this.name]=this;
}
}
new FieldShownAction();

View File

@@ -0,0 +1,2 @@
import { IAction } from "../types/ActionTypes";
export const actionAddins :Record<string,IAction>={};

View File

@@ -0,0 +1,53 @@
import { actionAddins } from ".";
import { IAction,IActionResult, IActionNode, IActionProperty, IField } from "../types/ActionTypes";
interface IMustInputProps{
field:IField;
message:string;
}
export class MustInputAction implements IAction{
name: string;
actionProps: IActionProperty[];
props:IMustInputProps;
constructor(){
this.name="必須チェック";
this.actionProps=[];
this.register();
this.props={
field:{code:''},
message:''
}
this.register();
}
async process(actionNode:IActionNode,event:any):Promise<IActionResult> {
let result={
canNext:true,
result:false
};
this.actionProps=actionNode.actionProps;
if (!('field' in actionNode.ActionValue) && !('message' in actionNode.ActionValue)) {
return result
}
this.props = actionNode.ActionValue as IMustInputProps;
const record = event.record;
const value = record[this.props.field.code]?.value;
if(value===undefined || value===''){
record[this.props.field.code].error=this.props.message;
return result;
}
result= {
canNext:true,
result:true
}
return result;
}
register(): void {
actionAddins[this.name]=this;
}
}
new MustInputAction();

View File

@@ -0,0 +1,71 @@
// export const sum = (a: number, b: number) => {
// if ('development' === process.env.NODE_ENV) {
// console.log('boop');
// }
// return a + b;
// };
import $ from 'jquery';
import { ActionProcess } from './types/action-process';
import { ActionFlow } from './types/ActionTypes';
import { FlowInfo } from './types/flowSetting';
declare const alcflow : {
[key:string]:FlowInfo
};
$(function (){
const getChangeEvents=(events:string[])=>{
return events.filter((event)=>event.includes(".change."));
}
const getClickEvents=(events:string[])=>{
return events.filter((event)=>event.includes(".customButtonClick."));
}
const getKintoneEvents=(events:string[])=>{
return events.filter((event)=>{
return !event.includes(".customButtonClick.") && !event.includes(".change.")
});
}
const events=Object.keys(alcflow);
const changeEvents = getChangeEvents(events);
const clickEvents = getClickEvents(events);
const kintoneEvents = getKintoneEvents(events);
if(kintoneEvents.length>0 ){
//通常Kintoneイベントをバンド
kintone.events.on(kintoneEvents,async (event:any)=>{
const flowinfo = alcflow[event.type];
const flow=ActionFlow.fromJSON(flowinfo.content);
if(flow!==undefined){
const process = new ActionProcess(event.type,flow,event);
await process.exec();
}
return event;
});
}
if(changeEvents.length>0){
//値変更イベントをバンドする
kintone.events.on(changeEvents,(event:any)=>{
const flowinfo = alcflow[event.type];
const flow=ActionFlow.fromJSON(flowinfo.content);
if(flow!==undefined){
const process = new ActionProcess(event.type,flow,event);
process.exec();
}
return event;
});
}
if(clickEvents.length>0){
clickEvents.forEach((eventName:string)=>{
$(document).on(eventName,async ()=>{
const event=kintone.app.record.get();
const flowinfo = alcflow[eventName];
const flow=ActionFlow.fromJSON(flowinfo.content);
if(flow!==undefined){
const process = new ActionProcess(eventName,flow,event);
await process.exec();
}
const record = event.record;
kintone.app.record.set({record})
});
});
}
});

402
plugin/kintone-addins/src/kintone.d.ts vendored Normal file
View File

@@ -0,0 +1,402 @@
/**
* Kintone APIタイプ定義
*/
declare namespace kintone {
namespace events {
function on(event: string | string[], handler: (event: any) => any): void;
function off(
event: string | string[],
handler: (event: any) => any
): boolean;
function off(event: string | string[]): boolean;
function off(): boolean;
}
namespace api {
function url(path: string, detectGuestSpace?: boolean): string;
function urlForGet(
path: string,
params: any,
detectGuestSpace?: boolean
): string;
function getConcurrencyLimit(): Promise<{
limit: number;
running: number;
}>;
}
function api(pathOrUrl: string, method: string, params: any): Promise<any>;
function api(
pathOrUrl: string,
method: string,
params: any,
callback: (resp: any) => void,
errback: (err: any) => void
): void;
function getRequestToken(): string;
function proxy(
url: string,
method: string,
headers: any,
data: any
): Promise<any>;
function proxy(
url: string,
method: string,
headers: any,
data: any,
callback: (resp: any) => void,
errback: (err: any) => void
): void;
class Promise<T> {
constructor(
callback: (
resolve: (resolved: T) => any,
reject: (rejected: any) => any
) => void
);
then(callback: (resolved: T) => any): Promise<any>;
catch(callback: (rejected: any) => any): Promise<any>;
static resolve(resolved: any): Promise<any>;
static reject(rejected: any): Promise<any>;
static all(listOfPromise: Array<Promise<any>>): Promise<any>;
}
namespace proxy {
function upload(
url: string,
method: string,
headers: any,
data: any,
callback: (resp: any) => void,
errback: (err: any) => void
): void;
function upload(
url: string,
method: string,
headers: any,
data: any
): Promise<any>;
}
namespace app {
function getFieldElements(fieldCode: string): HTMLElement[] | null;
function getHeaderMenuSpaceElement(): HTMLElement | null;
function getHeaderSpaceElement(): HTMLElement | null;
function getId(): number | null;
function getLookupTargetAppId(fieldCode: string): string | null;
function getQuery(): string | null;
function getQueryCondition(): string | null;
function getRelatedRecordsTargetAppId(fieldCode: string): string | null;
namespace record {
function getId(): number | null;
function get(): any | null;
function getHeaderMenuSpaceElement(): HTMLElement | null;
function getFieldElement(fieldCode: string): HTMLElement | null;
function set(record: any): void;
function getSpaceElement(id: string): HTMLElement | null;
function setFieldShown(fieldCode: string, isShown: boolean): void;
function setGroupFieldOpen(fieldCode: string, isOpen: boolean): void;
}
}
namespace mobile {
namespace app {
function getFieldElements(fieldCode: string): HTMLElement[] | null;
function getHeaderSpaceElement(): HTMLElement | null;
function getId(): number | null;
function getLookupTargetAppId(fieldCode: string): string | null;
function getQuery(): string | null;
function getQueryCondition(): string | null;
function getRelatedRecordsTargetAppId(fieldCode: string): string | null;
namespace record {
function getId(): number | null;
function get(): any | null;
function getFieldElement(fieldCode: string): HTMLElement | null;
function set(record: any): void;
function getSpaceElement(id: string): HTMLElement | null;
function setFieldShown(fieldCode: string, isShown: boolean): void;
function setGroupFieldOpen(fieldCode: string, isOpen: boolean): void;
}
}
namespace portal {
function getContentSpaceElement(): HTMLElement | null;
}
namespace space {
namespace portal {
function getContentSpaceElement(): HTMLElement | null;
}
}
}
namespace plugin {
namespace app {
function getConfig(pluginId: string): any;
function setConfig(config: any, callback?: () => void): void;
function proxy(
pluginId: string,
url: string,
method: string,
headers: any,
data: any
): Promise<any>;
function proxy(
pluginId: string,
url: string,
method: string,
headers: any,
data: any,
callback: (resp: any) => void,
error: (err: any) => void
): void;
function setProxyConfig(
url: string,
method: string,
headers: any,
data: any,
callback?: () => void
): void;
function getProxyConfig(url: string, method: string): any;
namespace proxy {
function upload(
pluginId: any,
url: string,
method: string,
headers: any,
data: any
): Promise<any>;
function upload(
pluginId: any,
url: string,
method: string,
headers: any,
data: any,
callback: (resp: any) => void,
error: (err: any) => void
): void;
}
}
}
namespace portal {
function getContentSpaceElement(): HTMLElement | null;
}
namespace space {
namespace portal {
function getContentSpaceElement(): HTMLElement | null;
}
}
interface LoginUser {
id: string;
code: string;
name: string;
email: string;
url: string;
employeeNumber: string;
phone: string;
mobilePhone: string;
extensionNumber: string;
timezone: string;
isGuest: boolean;
language: string;
}
function getLoginUser(): LoginUser;
function getUiVersion(): 1 | 2;
const $PLUGIN_ID: string;
namespace fieldTypes {
interface SingleLineText {
type?: "SINGLE_LINE_TEXT";
value: string;
disabled?: boolean;
error?: string;
}
interface RichText {
type?: "RICH_TEXT";
value: string;
disabled?: boolean;
error?: string;
}
interface MultiLineText {
type?: "MULTI_LINE_TEXT";
value: string;
disabled?: boolean;
error?: string;
}
interface Number {
type?: "NUMBER";
value: string;
disabled?: boolean;
error?: string;
}
interface Calc {
type: "CALC";
value: string;
disabled?: boolean;
}
interface RadioButton {
type?: "RADIO_BUTTON";
value: string;
disabled?: boolean;
error?: string;
}
interface DropDown {
type?: "DROP_DOWN";
value: string;
disabled?: boolean;
error?: string;
}
interface Date {
type?: "DATE";
value: string;
disabled?: boolean;
error?: string;
}
interface Time {
type?: "TIME";
value: string;
disabled?: boolean;
error?: string;
}
interface DateTime {
type?: "DATETIME";
value: string;
disabled?: boolean;
error?: string;
}
interface Link {
type?: "LINK";
value: string;
disabled?: boolean;
error?: string;
}
interface CheckBox {
type?: "CHECK_BOX";
value: string[];
disabled?: boolean;
error?: string;
}
interface MultiSelect {
type?: "MULTI_SELECT";
value: string[];
disabled?: boolean;
error?: string;
}
interface UserSelect {
type?: "USER_SELECT";
value: Array<{ code: string; name: string }>;
disabled?: boolean;
error?: string;
}
interface OrganizationSelect {
type?: "ORGANIZATION_SELECT";
value: Array<{ code: string; name: string }>;
disabled?: boolean;
error?: string;
}
interface GroupSelect {
type?: "GROUP_SELECT";
value: Array<{ code: string; name: string }>;
disabled?: boolean;
error?: string;
}
interface File {
type: "FILE";
value: Array<{
contentType: string;
fileKey: string;
name: string;
size: string;
}>;
disabled?: boolean;
error?: string;
}
interface Id {
type: "__ID__";
value: string;
}
interface Revision {
type: "__REVISION__";
value: string;
}
/**
* field type of UserField is MODIFIER.
* So error property not exists.
*/
interface Modifier {
type: "MODIFIER";
value: { code: string; name: string };
}
/**
* field type of UserField is CREATOR.
* So error property not exists.
*/
interface Creator {
type: "CREATOR";
value: { code: string; name: string };
}
interface RecordNumber {
type: "RECORD_NUMBER";
value: string;
error?: string;
}
interface UpdatedTime {
type: "UPDATED_TIME";
value: string;
error?: string;
}
interface CreatedTime {
type: "CREATED_TIME";
value: string;
error?: string;
}
}
}

View File

@@ -0,0 +1,270 @@
/**
* アプリ情報
*/
export interface AppInfo {
appId: string;
code?: string;
name: string;
description?: string;
}
/**
* アクションのプロパティ定義
*/
export interface IActionProperty {
component: string;
props: {
//プロパティ名
name: string;
//プロパティ表示名
displayName: string;
placeholder: string;
//プロパティ設定値
modelValue: any;
};
}
/**
* アクションタイプ定義
*/
export interface IActionNode {
id: string;
//アクション名
name: string;
title: string;
subTitle: string;
inputPoint: string;
//出力ポイント(条件分岐以外未使用)
outputPoints: Array<string>;
//ルートアクションKintone event
isRoot: boolean;
//アクションのプロパティ定義
actionProps: Array<IActionProperty>;
//アクションのプロパティ設定値抽出
ActionValue: object
prevNodeId?: string;
nextNodeIds: Map<string, string>;
}
/**
* アクションフローの定義
*/
export interface IActionFlow {
id: string;
actionNodes: IActionNode[];
findNodeById(id: string): IActionNode | undefined;
toJSON(): any;
getRoot(): IActionNode | undefined;
}
export interface IActionResult{
canNext:boolean;
result?:any;
}
/**
* コンテキスト
* レコードとフローの変数を持つ
*/
export interface IContext{
record:any,
variables:any
}
/**
* アクションのインターフェース
*/
export interface IAction{
//アクションの名前(ユーニック名が必要)
name:string;
//属性設定情報
actionProps: Array<IActionProperty>;
//アクションのプロセス実行関数
process(prop:IActionNode,event:any,context:IContext):Promise<IActionResult>;
//アクションの登録関数
register():void;
}
export interface IField{
name?:string;
code:string;
type?:string;
}
/**
* アクションのプロパティ定義に基づいたクラス
*/
export class ActionProperty implements IActionProperty {
component: string;
props: {
// プロパティ名
name: string;
// プロパティ表示名
displayName: string;
placeholder: string;
// プロパティ設定値
modelValue: any;
};
static defaultProperty(): IActionProperty {
return new ActionProperty('InputText', 'displayName', '表示名', '表示を入力してください', '');
};
constructor(
component: string,
name: string,
displayName: string,
placeholder: string,
modelValue: any
) {
this.component = component;
this.props = {
name: name,
displayName: displayName,
placeholder: placeholder,
modelValue: modelValue
};
}
}
/**
* IActionNodeの実装、RootActionNode以外のアクション定義
*/
export class ActionNode implements IActionNode {
id: string;
name: string;
get title(): string {
const prop = this.actionProps.find((prop) => prop.props.name === "displayName");
return prop?.props.modelValue;
};
get subTitle(): string {
return this.name;
};
inputPoint: string;
//出力ポイント(条件分岐以外未使用)
outputPoints: Array<string>;
actionProps: Array<IActionProperty>;
get isRoot(): boolean {
return false;
};
get ActionValue(): object {
const propValue: any = {};
this.actionProps.forEach((value) => {
propValue[value.props.name] = value.props.modelValue
});
return propValue;
};
prevNodeId?: string;
nextNodeIds: Map<string, string>;
constructor(
{id,name,title,inputPoint,outputPoints,actionProps}:
{
id:string,
name: string,
title: string,
inputPoint: string,
outputPoints: Array<string>,
actionProps: Array<IActionProperty>}
) {
this.id=id;
this.name = name;
this.inputPoint = inputPoint;
this.outputPoints = outputPoints;
const defProp = ActionProperty.defaultProperty();
defProp.props.modelValue = title;
this.actionProps = actionProps;
this.nextNodeIds = new Map<string, string>();
}
}
/**
* ルートアクション定義
*/
export class RootAction implements IActionNode {
id: string;
name: string;
title: string;
subTitle: string;
inputPoint: string;
//出力ポイント(条件分岐以外未使用)
outputPoints: Array<string>;
isRoot: boolean;
actionProps: Array<IActionProperty>;
ActionValue: object;
prevNodeId?: string = undefined;
nextNodeIds: Map<string, string>;
constructor(
{id,name,title,subTitle}:{
id:string,
name: string,
title: string,
subTitle: string}
) {
this.id = id;
this.name = name;
this.title = title;
this.subTitle = subTitle;
this.inputPoint = '';
this.outputPoints = [];
this.isRoot = true;
this.actionProps = [];
this.ActionValue = {};
this.nextNodeIds = new Map<string, string>();
}
}
/**
* アクションフローの定義
*/
export class ActionFlow implements IActionFlow {
id: string;
actionNodes: Array<IActionNode>;
constructor(actionNodes: Array<IActionNode> | RootAction) {
if (actionNodes instanceof Array) {
this.actionNodes = actionNodes;
} else {
this.actionNodes = [actionNodes];
}
this.id = '';
}
/***
* IDでActionNodeを取得する
*/
findNodeById(id: string): IActionNode | undefined {
return this.actionNodes.find((node) => node.id === id);
}
toJSON() {
return {
id: this.id,
actionNodes: this.actionNodes.map(node => {
const { nextNodeIds, ...rest } = node;
return {
...rest,
nextNodeIds: Array.from(nextNodeIds.entries())
};
})
};
}
getRoot(): IActionNode | undefined {
return this.actionNodes.find(node => node.isRoot)
}
static fromJSON(json: string): ActionFlow {
const parsedObject = JSON.parse(json);
const actionNodes = parsedObject.actionNodes.map((node: any) => {
const nodeClass = !node.isRoot ? new ActionNode(node)
: new RootAction(node);
nodeClass.nextNodeIds = new Map<string,string>(Object.entries(node.nextNodeIds));
nodeClass.prevNodeId = node.prevNodeId;
nodeClass.id = node.id;
return nodeClass;
});
const actionFlow = new ActionFlow(actionNodes);
actionFlow.id = parsedObject.id;
return actionFlow;
}
}

Some files were not shown because too many files have changed in this diff Show More