Compare commits
22 Commits
fix/condit
...
plugin-inf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3540becf6f | ||
|
|
63099eda8b | ||
|
|
65d89b0462 | ||
|
|
c6ded099fa | ||
|
|
c072233593 | ||
|
|
4e296c1555 | ||
| 016fcaab29 | |||
|
|
bebc1ec9fa | ||
|
|
f71c3d2123 | ||
|
|
d79ce8d06b | ||
|
|
fc9c3a5e81 | ||
|
|
6df72a1ae3 | ||
|
|
372dbe50f7 | ||
|
|
68fde6d490 | ||
|
|
c398dee21e | ||
|
|
f2ab310b6d | ||
|
|
ca0f24465b | ||
|
|
3cc4b65460 | ||
|
|
a6cf95b76d | ||
|
|
484ab9fdae | ||
|
|
78bba2502f | ||
|
|
c78b3cb5c0 |
File diff suppressed because one or more lines are too long
@@ -217,9 +217,7 @@ def deoployappfromkintone(app:str,revision:str,c:config.KINTONE_ENV):
|
|||||||
data = {"apps":[{"app":app,"revision":revision}],"revert": False}
|
data = {"apps":[{"app":app,"revision":revision}],"revert": False}
|
||||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||||
return r.json
|
return r.json
|
||||||
|
|
||||||
# 既定項目に含めるアプリのフィールドのみ取得する
|
|
||||||
# スペース、枠線、ラベルを含まない
|
|
||||||
def getfieldsfromkintone(app:str,c:config.KINTONE_ENV):
|
def getfieldsfromkintone(app:str,c:config.KINTONE_ENV):
|
||||||
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||||
params = {"app":app}
|
params = {"app":app}
|
||||||
@@ -227,44 +225,6 @@ def getfieldsfromkintone(app:str,c:config.KINTONE_ENV):
|
|||||||
r = httpx.get(url,headers=headers,params=params)
|
r = httpx.get(url,headers=headers,params=params)
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
# フォームに配置するフィールドのみ取得する
|
|
||||||
# スペース、枠線、ラベルも含める
|
|
||||||
def getformfromkintone(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}{config.API_V1_STR}/form.json"
|
|
||||||
r = httpx.get(url,headers=headers,params=params)
|
|
||||||
return r.json()
|
|
||||||
|
|
||||||
|
|
||||||
def merge_kintone_fields(fields_response: dict, form_response: dict) -> dict:
|
|
||||||
fields_properties = fields_response.get('properties', {})
|
|
||||||
form_properties = form_response.get('properties', [])
|
|
||||||
|
|
||||||
merged_properties = {k: v for k, v in fields_properties.items()}
|
|
||||||
|
|
||||||
for index, form_field in enumerate(form_properties):
|
|
||||||
code = form_field.get('code')
|
|
||||||
if code:
|
|
||||||
if code and code not in merged_properties:
|
|
||||||
merged_properties[code] = form_field
|
|
||||||
else:
|
|
||||||
element_id = form_field.get('elementId')
|
|
||||||
if element_id:
|
|
||||||
key = element_id
|
|
||||||
form_field['code']=element_id
|
|
||||||
form_field['label']=form_field.get('type')
|
|
||||||
# else:
|
|
||||||
# key = f"{form_field.get('type')}_{index}"
|
|
||||||
merged_properties[key] = form_field
|
|
||||||
|
|
||||||
merged_response = {
|
|
||||||
'revision': fields_response.get('revision', ''),
|
|
||||||
'properties': merged_properties
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged_response
|
|
||||||
|
|
||||||
def analysefields(excel,kintone):
|
def analysefields(excel,kintone):
|
||||||
updatefields={}
|
updatefields={}
|
||||||
addfields={}
|
addfields={}
|
||||||
@@ -404,12 +364,6 @@ def updateappjscss(app,uploads,c:config.KINTONE_ENV):
|
|||||||
r = httpx.put(url,headers=headers,data=json.dumps(data))
|
r = httpx.put(url,headers=headers,data=json.dumps(data))
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
def getTempPath(filename):
|
|
||||||
scriptdir = Path(__file__).resolve().parent
|
|
||||||
rootdir = scriptdir.parent.parent.parent.parent
|
|
||||||
fpath = os.path.join(rootdir,"Temp",filename)
|
|
||||||
return fpath
|
|
||||||
|
|
||||||
def createappjs(domainid,app):
|
def createappjs(domainid,app):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
flows = get_flows_by_app(db,domainid,app)
|
flows = get_flows_by_app(db,domainid,app)
|
||||||
@@ -418,10 +372,10 @@ def createappjs(domainid,app):
|
|||||||
for flow in flows:
|
for flow in flows:
|
||||||
content[flow.eventid] = {'flowid':flow.flowid,'name':flow.name,'content':flow.content}
|
content[flow.eventid] = {'flowid':flow.flowid,'name':flow.name,'content':flow.content}
|
||||||
js = 'const alcflow=' + json.dumps(content)
|
js = 'const alcflow=' + json.dumps(content)
|
||||||
# scriptdir = Path(__file__).resolve().parent
|
scriptdir = Path(__file__).resolve().parent
|
||||||
# rootdir = scriptdir.parent.parent.parent.parent
|
rootdir = scriptdir.parent.parent.parent.parent
|
||||||
# fpath = os.path.join(rootdir,"Temp",f"alc_setting_{app}.js")
|
fpath = os.path.join(rootdir,"Temp",f"alc_setting_{app}.js")
|
||||||
fpath = getTempPath(f"alc_setting_{app}.js")
|
print(rootdir)
|
||||||
print(fpath)
|
print(fpath)
|
||||||
with open(fpath,'w') as file:
|
with open(fpath,'w') as file:
|
||||||
file.write(js)
|
file.write(js)
|
||||||
@@ -509,7 +463,7 @@ async def app(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkintoneenv
|
|||||||
r = httpx.get(url,headers=headers,params=params)
|
r = httpx.get(url,headers=headers,params=params)
|
||||||
return r.json()
|
return r.json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:app',request.url._url, f"Error occurred while get app({c.DOMAIN_NAME}->{app}):",e)
|
raise APIException('kintone:app',request.url._url, f"Error occurred while get app({c.DOMAIN_NAM}->{app}):",e)
|
||||||
|
|
||||||
@r.get("/allapps")
|
@r.get("/allapps")
|
||||||
async def allapps(request:Request,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
async def allapps(request:Request,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||||
@@ -519,30 +473,21 @@ async def allapps(request:Request,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
|||||||
r = httpx.get(url,headers=headers)
|
r = httpx.get(url,headers=headers)
|
||||||
return r.json()
|
return r.json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:allapps',request.url._url, f"Error occurred while get allapps({c.DOMAIN_NAME}):",e)
|
raise APIException('kintone:allapps',request.url._url, f"Error occurred while get allapps({c.DOMAIN_NAM}):",e)
|
||||||
|
|
||||||
@r.get("/appfields")
|
@r.get("/appfields")
|
||||||
async def appfields(request:Request,app:str,env = Depends(getkintoneenv)):
|
async def appfields(request:Request,app:str,env = Depends(getkintoneenv)):
|
||||||
try:
|
try:
|
||||||
return getfieldsfromkintone(app,env)
|
return getfieldsfromkintone(app,env)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:appfields',request.url._url, f"Error occurred while get app fileds({env.DOMAIN_NAME}->{app}):",e)
|
raise APIException('kintone:appfields',request.url._url, f"Error occurred while get app fileds({env.DOMAIN_NAM}->{app}):",e)
|
||||||
|
|
||||||
@r.get("/allfields")
|
|
||||||
async def allfields(request:Request,app:str,env = Depends(getkintoneenv)):
|
|
||||||
try:
|
|
||||||
field_resp = getfieldsfromkintone(app,env)
|
|
||||||
form_resp = getformfromkintone(app,env)
|
|
||||||
return merge_kintone_fields(field_resp,form_resp)
|
|
||||||
except Exception as e:
|
|
||||||
raise APIException('kintone:allfields',request.url._url, f"Error occurred while get form fileds({env.DOMAIN_NAME}->{app}):",e)
|
|
||||||
|
|
||||||
@r.get("/appprocess")
|
@r.get("/appprocess")
|
||||||
async def appprocess(request:Request,app:str,env = Depends(getkintoneenv)):
|
async def appprocess(request:Request,app:str,env = Depends(getkintoneenv)):
|
||||||
try:
|
try:
|
||||||
return getprocessfromkintone(app,env)
|
return getprocessfromkintone(app,env)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:appprocess',request.url._url, f"Error occurred while get app process({env.DOMAIN_NAME}->{app}):",e)
|
raise APIException('kintone:appprocess',request.url._url, f"Error occurred while get app process({env.DOMAIN_NAM}->{app}):",e)
|
||||||
|
|
||||||
@r.get("/alljscss")
|
@r.get("/alljscss")
|
||||||
async def alljscs(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
async def alljscs(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||||
@@ -553,7 +498,7 @@ async def alljscs(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkinton
|
|||||||
r = httpx.get(url,headers=headers,params=params)
|
r = httpx.get(url,headers=headers,params=params)
|
||||||
return r.json()
|
return r.json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:alljscss',request.url._url, f"Error occurred while get app js/css({c.DOMAIN_NAME}->{app}):",e)
|
raise APIException('kintone:alljscss',request.url._url, f"Error occurred while get app js/css({c.DOMAIN_NAM}->{app}):",e)
|
||||||
|
|
||||||
@r.post("/createapp",)
|
@r.post("/createapp",)
|
||||||
async def createapp(request:Request,name:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
async def createapp(request:Request,name:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
|
||||||
@@ -569,7 +514,7 @@ async def createapp(request:Request,name:str,c:config.KINTONE_ENV=Depends(getkin
|
|||||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||||
return r.json
|
return r.json
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({c.DOMAIN_NAME}->{name}):",e)
|
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({c.DOMAIN_NAM}->{name}):",e)
|
||||||
|
|
||||||
|
|
||||||
@r.post("/createappfromexcel",)
|
@r.post("/createappfromexcel",)
|
||||||
@@ -698,7 +643,7 @@ async def updateprocessfromexcel(request:Request,app:str,env = Depends(getkinton
|
|||||||
if deploy:
|
if deploy:
|
||||||
result = deoployappfromkintone(app,revision,env)
|
result = deoployappfromkintone(app,revision,env)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:updateprocessfromexcel',request.url._url, f"Error occurred while update process ({env.DOMAIN_NAME}->{app}):",e)
|
raise APIException('kintone:updateprocessfromexcel',request.url._url, f"Error occurred while update process ({env.DOMAIN_NAM}->{app}):",e)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -709,8 +654,7 @@ async def createjstokintone(request:Request,app:str,env:config.KINTONE_ENV = Dep
|
|||||||
jscs=[]
|
jscs=[]
|
||||||
files=[]
|
files=[]
|
||||||
files.append(createappjs(env.DOMAIN_ID, app))
|
files.append(createappjs(env.DOMAIN_ID, app))
|
||||||
files.append(getTempPath('alc_runtime.js'))
|
files.append('Temp\\alc_runtime.js')
|
||||||
files.append(getTempPath('alc_runtime.css'))
|
|
||||||
for file in files:
|
for file in files:
|
||||||
upload = uploadkintonefiles(file,env)
|
upload = uploadkintonefiles(file,env)
|
||||||
if upload.get('fileKey') != None:
|
if upload.get('fileKey') != None:
|
||||||
@@ -720,4 +664,4 @@ async def createjstokintone(request:Request,app:str,env:config.KINTONE_ENV = Dep
|
|||||||
deoployappfromkintone(app,appjscs["revision"],env)
|
deoployappfromkintone(app,appjscs["revision"],env)
|
||||||
return appjscs
|
return appjscs
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise APIException('kintone:createjstokintone',request.url._url, f"Error occurred while create js ({env.DOMAIN_NAME}->{app}):",e)
|
raise APIException('kintone:createjstokintone',request.url._url, f"Error occurred while create js ({env.DOMAIN_NAM}->{app}):",e)
|
||||||
|
|||||||
@@ -5,21 +5,22 @@ PROJECT_NAME = "KintoneAppBuilder"
|
|||||||
|
|
||||||
#SQLALCHEMY_DATABASE_URI = "postgres://maxz64:m@xz1205@alicornkintone.postgres.database.azure.com/postgres"
|
#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"
|
SQLALCHEMY_DATABASE_URI = "postgres://kabAdmin:P@ssw0rd!@kintonetooldb.postgres.database.azure.com/postgres"
|
||||||
#SQLALCHEMY_DATABASE_URI = "postgres://kabAdmin:P@ssw0rd!@kintonetooldb.postgres.database.azure.com/unittest"
|
|
||||||
API_V1_STR = "/k/v1"
|
API_V1_STR = "/k/v1"
|
||||||
|
|
||||||
API_V1_AUTH_KEY = "X-Cybozu-Authorization"
|
API_V1_AUTH_KEY = "X-Cybozu-Authorization"
|
||||||
|
|
||||||
DEPLOY_MODE = "PROD" #DEV,PROD
|
DEPLOY_MODE = "DEV" #DEV,PROD
|
||||||
|
|
||||||
DEPLOY_JS_URL = "https://ka-addin.azurewebsites.net/alc_runtime.js"
|
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_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']
|
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:
|
class KINTONE_ENV:
|
||||||
|
|
||||||
BASE_URL = ""
|
BASE_URL = ""
|
||||||
|
|
||||||
API_V1_AUTH_VALUE = ""
|
API_V1_AUTH_VALUE = ""
|
||||||
|
|||||||
@@ -24,8 +24,4 @@ python -m venv env
|
|||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
```
|
```
|
||||||
4. backend 起動
|
|
||||||
```bash
|
|
||||||
uvicorn app.main:app --reload
|
|
||||||
```
|
|
||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,6 +1,3 @@
|
|||||||
#開発環境
|
KAB_BACKEND_URL="https://kab-backend.azurewebsites.net/"
|
||||||
#KAB_BACKEND_URL="https://kab-backend.azurewebsites.net/"
|
#KAB_BACKEND_URL="http://127.0.0.1:8000/"
|
||||||
#単体テスト環境
|
|
||||||
#KAB_BACKEND_URL="https://kab-backend-unittest.azurewebsites.net/"
|
|
||||||
#ローカル開発環境
|
|
||||||
KAB_BACKEND_URL="http://127.0.0.1:8000/"
|
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ module.exports = configure(function (/* ctx */) {
|
|||||||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
|
||||||
devServer: {
|
devServer: {
|
||||||
// https: true
|
// https: true
|
||||||
port:9001,
|
|
||||||
open: true, // opens browser window automatically
|
open: true, // opens browser window automatically
|
||||||
env: { ...dotenv },
|
env: { ...dotenv },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div class="q-mb-xs q-ml-md text-primary">アプリ選択</div>
|
<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 class="q-pa-md row" style="border: 1px solid rgba(0, 0, 0, 0.12); border-radius: 4px;">
|
||||||
<div v-if="selField?.app && !showSelectApp">{{ selField?.app?.name }}</div>
|
<div v-if="selField?.app && !showSelectApp">{{ selField.app?.name }}</div>
|
||||||
<q-space />
|
<q-space />
|
||||||
<div>
|
<div>
|
||||||
<q-btn outline dense label="選 択" padding="none sm" color="primary" @click="() => {
|
<q-btn outline dense label="選 択" padding="none sm" color="primary" @click="() => {
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!showSelectApp && selField?.app?.name">
|
<div v-if="!showSelectApp && selField.app?.name">
|
||||||
<div>
|
<div>
|
||||||
<div class="row q-mb-md">
|
<div class="row q-mb-md">
|
||||||
<!-- <div class="col"> -->
|
<!-- <div class="col"> -->
|
||||||
@@ -31,9 +31,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<field-select ref="fieldDlg" name="フィールド" :type="selectType" :updateSelectFields="updateSelectFields"
|
<field-select ref="fieldDlg" name="フィールド" :type="selectType" :updateSelects="updateItems"
|
||||||
:appId="selField?.app?.id" not_page :filter="fieldFilter"
|
:appId="selField.app?.id" not_page :filter="fieldFilter"
|
||||||
:selectedFields="selField.fields" :fieldTypes="fieldTypes"></field-select>
|
:selFields="selField.fields"></field-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<show-dialog v-model:visible="showSelectApp" name="アプリ選択">
|
<show-dialog v-model:visible="showSelectApp" name="アプリ選択" @close="closeAppDlg">
|
||||||
<template v-slot:toolbar>
|
<template v-slot:toolbar>
|
||||||
<q-input dense debounce="300" v-model="filter" placeholder="検索" clearable>
|
<q-input dense debounce="300" v-model="filter" placeholder="検索" clearable>
|
||||||
<template v-slot:before>
|
<template v-slot:before>
|
||||||
@@ -51,14 +51,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<AppSelectBox ref="appDlg" name="アプリ" type="single" :filter="filter"
|
<AppSelectBox ref="appDlg" name="アプリ" type="single" :filter="filter"
|
||||||
:updateSelectApp="updateSelectApp"></AppSelectBox>
|
:updateExternalSelectAppInfo="updateExternalSelectAppInfo"></AppSelectBox>
|
||||||
</show-dialog>
|
</show-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, watchEffect, computed, reactive } from 'vue';
|
import { defineComponent, ref, watchEffect, computed ,reactive} from 'vue';
|
||||||
import ShowDialog from './ShowDialog.vue';
|
import ShowDialog from './ShowDialog.vue';
|
||||||
import FieldSelect from './FieldSelect.vue';
|
import FieldSelect from './FieldSelect.vue';
|
||||||
|
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||||
import AppSelectBox from './AppSelectBox.vue';
|
import AppSelectBox from './AppSelectBox.vue';
|
||||||
interface IApp {
|
interface IApp {
|
||||||
id: string,
|
id: string,
|
||||||
@@ -84,7 +85,7 @@ export default defineComponent({
|
|||||||
AppSelectBox,
|
AppSelectBox,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
selectedField: {
|
selectedField: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
@@ -92,24 +93,40 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'single'
|
default: 'single'
|
||||||
},
|
},
|
||||||
fieldTypes:{
|
|
||||||
type:Array,
|
|
||||||
default:()=>[]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
const appDlg = ref();
|
||||||
|
const fieldDlg = ref();
|
||||||
const showSelectApp = ref(false);
|
const showSelectApp = ref(false);
|
||||||
const selField = reactive(props.selectedField);
|
const selField = reactive(props.selectedField);
|
||||||
|
console.log(props.selectedField);
|
||||||
|
|
||||||
|
const store = useFlowEditorStore();
|
||||||
|
|
||||||
const isSelected = computed(() => {
|
const isSelected = computed(() => {
|
||||||
return selField !== null && typeof selField === 'object' && ('app' in selField)
|
return selField !== null && typeof selField === 'object' && ('app' in selField)
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateSelectApp = (newAppinfo: IApp) => {
|
|
||||||
|
const closeAppDlg = (val: string) => {
|
||||||
|
if (val == 'OK') {
|
||||||
|
selField.app = appDlg.value.selected[0];
|
||||||
|
selField.fields = [];
|
||||||
|
showSelectApp.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeFieldDialog = (val: string) => {
|
||||||
|
if (val == 'OK') {
|
||||||
|
selField.fields = fieldDlg.value.selected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const updateExternalSelectAppInfo = (newAppinfo: IApp) => {
|
||||||
selField.app = newAppinfo
|
selField.app = newAppinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateSelectFields = (newFields: IField[]) => {
|
const updateItems = (newFields: IField[]) => {
|
||||||
selField.fields = newFields
|
selField.fields = newFields
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,11 +135,15 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
appDlg,
|
||||||
|
fieldDlg,
|
||||||
|
closeAppDlg,
|
||||||
|
closeFieldDialog,
|
||||||
showSelectApp,
|
showSelectApp,
|
||||||
isSelected,
|
isSelected,
|
||||||
updateSelectApp,
|
updateExternalSelectAppInfo,
|
||||||
filter: ref(),
|
filter: ref(),
|
||||||
updateSelectFields,
|
updateItems,
|
||||||
fieldFilter: ref(),
|
fieldFilter: ref(),
|
||||||
selField
|
selField
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default {
|
|||||||
name: String,
|
name: String,
|
||||||
type: String,
|
type: String,
|
||||||
filter: String,
|
filter: String,
|
||||||
updateSelectApp: {
|
updateExternalSelectAppInfo: {
|
||||||
type: Function
|
type: Function
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -42,8 +42,8 @@ export default {
|
|||||||
const selected = ref([])
|
const selected = ref([])
|
||||||
|
|
||||||
watchEffect(()=>{
|
watchEffect(()=>{
|
||||||
if (selected.value && selected.value[0] && props.updateSelectApp) {
|
if (selected.value && selected.value[0] && props.updateExternalSelectAppInfo) {
|
||||||
props.updateSelectApp(selected.value[0])
|
props.updateExternalSelectAppInfo(selected.value[0])
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -1,144 +1,96 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-field labelColor="primary" class="condition-object" dense outlined :label="label" :disable="disabled"
|
<q-field v-model="selectedObject" labelColor="primary" class="condition-object"
|
||||||
:clearable="isSelected">
|
:clearable="isSelected" stack-label :dense="true" :outlined="true" >
|
||||||
<template v-slot:control>
|
<template v-slot:control >
|
||||||
<q-chip color="primary" text-color="white" v-if="isSelected && selectedObject.objectType==='field'" :dense="true" class="selected-obj">
|
<q-chip color="primary" text-color="white" v-if="isSelected && selectedObject.objectType==='field'" :dense="true" class="selected-obj">
|
||||||
{{ selectedObject.name }}
|
{{ selectedObject.name }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
<q-chip color="info" text-color="white" v-if="isSelected && selectedObject.objectType==='variable'" :dense="true" class="selected-obj">
|
<q-chip color="info" text-color="white" v-if="isSelected && selectedObject.objectType==='variable'" :dense="true" class="selected-obj">
|
||||||
{{ selectedObject.name.name }}
|
{{ selectedObject.name.name }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
<div v-if="isSelected && selectedObject.objectType==='text'">{{ selectedObject?.sharedText }}</div>
|
</template>
|
||||||
</template>
|
<template v-slot:append>
|
||||||
<template v-slot:append>
|
<q-icon name="search" class="cursor-pointer" @click="showDg"/>
|
||||||
<q-icon name="search" class="cursor-pointer" @click="showDg" />
|
</template>
|
||||||
</template>
|
</q-field>
|
||||||
</q-field>
|
<show-dialog v-model:visible="show" name="設定項目一覧" @close="closeDg" min-width="400px">
|
||||||
<show-dialog v-model:visible="show" name="設定項目" @close="closeDg" min-width="400px">
|
<template v-slot:toolbar>
|
||||||
<!-- <template v-slot:toolbar>
|
<q-input dense debounce="200" v-model="filter" placeholder="検索" clearable>
|
||||||
<q-input dense debounce="200" v-model="filter" placeholder="検索" clearable>
|
<template v-slot:before>
|
||||||
<template v-slot:before>
|
<q-icon name="search" />
|
||||||
<q-icon name="search" />
|
</template>
|
||||||
</template>
|
</q-input>
|
||||||
</q-input>
|
</template>
|
||||||
</template>
|
<condition-objects ref="appDg" name="フィールド" type="single" :filter="filter" :appId="store.appInfo?.appId" :vars="vars"></condition-objects>
|
||||||
<condition-objects ref="appDg" name="フィールド" type="single" :filter="filter" :appId="store.appInfo?.appId" :vars="vars"></condition-objects>
|
|
||||||
-->
|
|
||||||
<DynamicItemInput v-model:selectedObject="selectedObject" :canInput="config.canInput"
|
|
||||||
:buttonsConfig="config.buttonsConfig" :appId="store.appInfo?.appId" :options="options" ref="inputRef" />
|
|
||||||
|
|
||||||
</show-dialog>
|
</show-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, ref, watchEffect, computed ,PropType} from 'vue';
|
import { defineComponent, reactive, ref ,watchEffect,computed} from 'vue';
|
||||||
import ShowDialog from '../ShowDialog.vue';
|
import ShowDialog from '../ShowDialog.vue';
|
||||||
// import ConditionObjects from '../ConditionObjects.vue';
|
import ConditionObjects from '../ConditionObjects.vue';
|
||||||
import DynamicItemInput from '../DynamicItemInput/DynamicItemInput.vue';
|
import { useFlowEditorStore } from '../../stores/flowEditor';
|
||||||
import { useFlowEditorStore } from '../../stores/flowEditor';
|
import {IActionFlow,IActionNode,IActionVariable} from '../../types/ActionTypes';
|
||||||
import { IActionFlow, IActionNode, IActionVariable } from '../../types/ActionTypes';
|
export default defineComponent({
|
||||||
import { IDynamicInputConfig } from 'src/types/ComponentTypes';
|
name: 'ConditionObject',
|
||||||
|
components: {
|
||||||
|
ShowDialog,
|
||||||
export default defineComponent({
|
ConditionObjects
|
||||||
name: 'ConditionObject',
|
|
||||||
components: {
|
|
||||||
ShowDialog,
|
|
||||||
DynamicItemInput,
|
|
||||||
// ConditionObjects
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
},
|
||||||
label: {
|
props: {
|
||||||
type: String,
|
modelValue: {
|
||||||
default: undefined
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
config: {
|
setup(props, { emit }) {
|
||||||
type: Object as PropType<IDynamicInputConfig>,
|
const appDg = ref();
|
||||||
default: () => {
|
const show = ref(false);
|
||||||
return {
|
const selectedObject = ref(props.modelValue);
|
||||||
canInput: false,
|
const store = useFlowEditorStore();
|
||||||
buttonsConfig: [
|
const isSelected = computed(()=>{
|
||||||
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
|
return selectedObject.value!==null && typeof selectedObject.value === 'object' && ('name' in selectedObject.value)
|
||||||
{ label: '変数', color: 'green', type: 'VariableAdd' },
|
});
|
||||||
]
|
let vars:IActionVariable[] =[];
|
||||||
};
|
if(store.currentFlow!==undefined && store.activeNode!==undefined ){
|
||||||
|
vars =store.currentFlow.getVarNames(store.activeNode);
|
||||||
}
|
}
|
||||||
},
|
const filter=ref('');
|
||||||
|
const showDg = () => {
|
||||||
|
show.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
options:
|
const closeDg = (val:string) => {
|
||||||
{
|
if (val == 'OK') {
|
||||||
type:Array as PropType< string[]>,
|
selectedObject.value = appDg.value.selected[0];
|
||||||
default:()=>[]
|
}
|
||||||
},
|
};
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
watchEffect(() => {
|
||||||
default: null
|
emit('update:modelValue', selectedObject.value);
|
||||||
},
|
});
|
||||||
},
|
|
||||||
setup(props, { emit }) {
|
return {
|
||||||
// const appDg = ref();
|
store,
|
||||||
const inputRef=ref();
|
appDg,
|
||||||
const show = ref(false);
|
show,
|
||||||
const selectedObject = ref(props.modelValue);
|
showDg,
|
||||||
const store = useFlowEditorStore();
|
closeDg,
|
||||||
// const sharedText = ref(''); // 共享的文本状态
|
selectedObject,
|
||||||
const isSelected = computed(() => {
|
vars:reactive(vars),
|
||||||
return selectedObject.value?.sharedText !== '';
|
isSelected,
|
||||||
});
|
filter
|
||||||
// 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 = () => {
|
</script>
|
||||||
show.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeDg = (val: string) => {
|
|
||||||
if (val == 'OK') {
|
|
||||||
// selectedObject.value = appDg.value.selected[0];
|
|
||||||
selectedObject.value = inputRef.value.selectedObjectRef
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
emit('update:modelValue', selectedObject.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
inputRef,
|
|
||||||
store,
|
|
||||||
// appDg,
|
|
||||||
show,
|
|
||||||
showDg,
|
|
||||||
closeDg,
|
|
||||||
selectedObject,
|
|
||||||
vars: reactive(vars),
|
|
||||||
isSelected,
|
|
||||||
buttonsConfig: [
|
|
||||||
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
|
|
||||||
{ label: '変数', color: 'green', type: 'VariableAdd' },
|
|
||||||
]
|
|
||||||
// filter
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.condition-object {
|
.condition-object{
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
.selected-obj{
|
||||||
.selected-obj {
|
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -67,21 +67,17 @@
|
|||||||
<!-- condition -->
|
<!-- condition -->
|
||||||
<div @click.stop @keypress.stop v-else >
|
<div @click.stop @keypress.stop v-else >
|
||||||
<div class="row no-wrap items-center q-my-xs">
|
<div class="row no-wrap items-center q-my-xs">
|
||||||
<ConditionObject v-bind="prop.node" v-model="prop.node.object" :config="leftDynamicItemConfig" class="col-4"/>
|
<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-select v-model="prop.node.operator" :options="operators" class="operator" :outlined="true" :dense="true"></q-select>
|
||||||
<ConditionObject v-bind="prop.node" v-model="prop.node.value" :config="rightDynamicItemConfig" class="col-4"
|
<q-input v-if="!prop.node.object || !('options' in prop.node.object)"
|
||||||
:options="objectValueOptions(prop.node?.object?.options)"
|
|
||||||
/>
|
|
||||||
<!-- <ConditionObject v-bind="prop.node" v-model="prop.node.object" class="col-4"/> -->
|
|
||||||
<!-- <q-input v-if="!prop.node.object || !('options' in prop.node.object)"
|
|
||||||
v-model="prop.node.value"
|
v-model="prop.node.value"
|
||||||
class="condition-value" :outlined="true" :dense="true" ></q-input> -->
|
class="condition-value" :outlined="true" :dense="true" ></q-input>
|
||||||
<!-- <q-select v-if="prop.node.object && ('options' in prop.node.object)"
|
<q-select v-if="prop.node.object && ('options' in prop.node.object)"
|
||||||
v-model="prop.node.value"
|
v-model="prop.node.value"
|
||||||
:options="objectValueOptions(prop.node.object.options)"
|
:options="objectValueOptions(prop.node.object.options)"
|
||||||
clearable
|
clearable
|
||||||
value-key="index"
|
value-key="index"
|
||||||
class="condition-value" :outlined="true" :dense="true" ></q-select> -->
|
class="condition-value" :outlined="true" :dense="true" ></q-select>
|
||||||
<q-btn flat round dense icon="more_horiz" size="sm" >
|
<q-btn flat round dense icon="more_horiz" size="sm" >
|
||||||
<q-menu auto-close anchor="top right">
|
<q-menu auto-close anchor="top right">
|
||||||
<q-list>
|
<q-list>
|
||||||
@@ -120,7 +116,6 @@ import { finished } from 'stream';
|
|||||||
import { defineComponent,ref,reactive, computed, inject } from 'vue';
|
import { defineComponent,ref,reactive, computed, inject } from 'vue';
|
||||||
import { INode,ConditionTree,GroupNode,ConditionNode, LogicalOperator,Operator,NodeType } from '../../types/Conditions';
|
import { INode,ConditionTree,GroupNode,ConditionNode, LogicalOperator,Operator,NodeType } from '../../types/Conditions';
|
||||||
import ConditionObject from './ConditionObject.vue';
|
import ConditionObject from './ConditionObject.vue';
|
||||||
import { IDynamicInputConfig } from 'src/types/ComponentTypes';
|
|
||||||
export default defineComponent( {
|
export default defineComponent( {
|
||||||
name: 'NodeCondition',
|
name: 'NodeCondition',
|
||||||
components: {
|
components: {
|
||||||
@@ -148,18 +143,17 @@ export default defineComponent( {
|
|||||||
return opts;
|
return opts;
|
||||||
});
|
});
|
||||||
|
|
||||||
const operatorSet = inject<Array<any>>('Operator')
|
const operator = inject('Operator')
|
||||||
const operators = ref(operatorSet ? operatorSet : Object.values(Operator));
|
const operators =computed(()=>{
|
||||||
|
return operator ? operator : Object.values(Operator);
|
||||||
|
});
|
||||||
const tree = reactive(props.conditionTree);
|
const tree = reactive(props.conditionTree);
|
||||||
|
|
||||||
const conditionString = computed(()=>{
|
const conditionString = computed(()=>{
|
||||||
return tree.buildConditionString(tree.root);
|
return tree.buildConditionString(tree.root);
|
||||||
});
|
});
|
||||||
|
|
||||||
const objectValueOptions=(options:any):any[]|null=>{
|
const objectValueOptions=(options:any):any[]=>{
|
||||||
if(!options){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const opts:any[] =[];
|
const opts:any[] =[];
|
||||||
Object.keys(options).forEach((key) =>
|
Object.keys(options).forEach((key) =>
|
||||||
{
|
{
|
||||||
@@ -226,14 +220,11 @@ export default defineComponent( {
|
|||||||
ticked.value=[];
|
ticked.value=[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const expanded=computed(()=>tree.getGroups(tree.root));
|
const expanded=computed(()=>tree.getGroups(tree.root));
|
||||||
// addCondition(tree.root);
|
// addCondition(tree.root);
|
||||||
const leftDynamicItemConfig = inject<IDynamicInputConfig>('leftDynamicItemConfig');
|
|
||||||
const rightDynamicItemConfig = inject<IDynamicInputConfig>('rightDynamicItemConfig');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
leftDynamicItemConfig,
|
|
||||||
rightDynamicItemConfig,
|
|
||||||
showingCondition,
|
showingCondition,
|
||||||
conditionString,
|
conditionString,
|
||||||
tree,
|
tree,
|
||||||
@@ -265,12 +256,10 @@ export default defineComponent( {
|
|||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.operator{
|
.operator{
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,161 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="q-mx-md" style="max-width: 600px;">
|
|
||||||
<!-- <q-card> -->
|
|
||||||
<div class="q-mb-md">
|
|
||||||
<q-input ref="inputRef" v-if="!optionsRef|| optionsRef.length===0"
|
|
||||||
outlined dense debounce="200" @update:model-value="updateSharedText"
|
|
||||||
v-model="sharedText" :readonly="!canInputFlag" autogrow>
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-btn flat round padding="none" icon="cancel" @click="clearSharedText" color="grey-6" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<q-select v-if="optionsRef && optionsRef.length>0"
|
|
||||||
:model-value="sharedText"
|
|
||||||
:options="optionsRef"
|
|
||||||
clearable
|
|
||||||
value-key="index"
|
|
||||||
outlined
|
|
||||||
dense
|
|
||||||
use-input
|
|
||||||
hide-selected
|
|
||||||
input-debounce="10"
|
|
||||||
fill-input
|
|
||||||
@input-value="setValue"
|
|
||||||
@clear="sharedText=null"
|
|
||||||
hide-dropdown-icon
|
|
||||||
:readonly="!canInputFlag"
|
|
||||||
>
|
|
||||||
</q-select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row q-gutter-sm">
|
|
||||||
<q-btn v-for="button in buttonsConfig" :key="button.type" :color="button.color" @mousedown.prevent
|
|
||||||
@click="openDialog(button)" size="sm">
|
|
||||||
{{ button.label }}
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<show-dialog v-model:visible="dialogVisible" :name="currentDialogName" @close="closeDialog" min-width="400px">
|
|
||||||
<template v-slot:toolbar>
|
|
||||||
<q-input dense debounce="200" v-model="filter" clearable>
|
|
||||||
<template v-slot:before>
|
|
||||||
<q-icon name="search" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
</template>
|
|
||||||
<!-- asdf -->
|
|
||||||
<component :is="currentComponent" @select="handleSelect" :filter="filter" :appId="appId" />
|
|
||||||
</show-dialog>
|
|
||||||
<!-- </q-card> -->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { ref, inject, watchEffect, defineComponent,PropType } from 'vue';
|
|
||||||
import FieldAdd from './FieldAdd.vue';
|
|
||||||
import VariableAdd from './VariableAdd.vue';
|
|
||||||
// import FunctionAdd from './FunctionAdd.vue';
|
|
||||||
import ShowDialog from '../ShowDialog.vue';
|
|
||||||
import { IButtonConfig } from 'src/types/ComponentTypes';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'DynamicItemInput',
|
|
||||||
components: {
|
|
||||||
FieldAdd,
|
|
||||||
VariableAdd,
|
|
||||||
// FunctionAdd,
|
|
||||||
ShowDialog
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
canInput: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
appId: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
selectedObject: {
|
|
||||||
default: {}
|
|
||||||
},
|
|
||||||
options:{
|
|
||||||
type:Array as PropType< string[]>
|
|
||||||
},
|
|
||||||
buttonsConfig: {
|
|
||||||
type: Array as PropType<IButtonConfig[]>,
|
|
||||||
default: () => [
|
|
||||||
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const filter = ref('');
|
|
||||||
const dialogVisible = ref(false);
|
|
||||||
const currentDialogName = ref('');
|
|
||||||
const selectedObjectRef = ref(props.selectedObject);
|
|
||||||
const currentComponent = ref('FieldAdd');
|
|
||||||
const sharedText = ref(props.selectedObject?.sharedText ?? '');
|
|
||||||
const inputRef = ref();
|
|
||||||
const canInputFlag = ref(props.canInput);
|
|
||||||
const editable = ref(false);
|
|
||||||
|
|
||||||
const openDialog = (button: IButtonConfig) => {
|
|
||||||
currentDialogName.value = button.label;
|
|
||||||
currentComponent.value = button.type;
|
|
||||||
dialogVisible.value = true;
|
|
||||||
editable.value = canInputFlag.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeDialog = () => {
|
|
||||||
dialogVisible.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelect = (value:any) => {
|
|
||||||
|
|
||||||
if (value && value._t && (value._t as string).length > 0) {
|
|
||||||
canInputFlag.value = editable.value;
|
|
||||||
}
|
|
||||||
selectedObjectRef.value={ sharedText: value._t, ...value };
|
|
||||||
sharedText.value = `${value._t}`;
|
|
||||||
// emit('update:selectedObject', { sharedText: sharedText.value, ...value });
|
|
||||||
dialogVisible.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearSharedText = () => {
|
|
||||||
sharedText.value = '';
|
|
||||||
selectedObjectRef.value={};
|
|
||||||
canInputFlag.value = true;
|
|
||||||
// emit('update:selectedObject', {});
|
|
||||||
}
|
|
||||||
const updateSharedText = (value:string) => {
|
|
||||||
sharedText.value = value;
|
|
||||||
selectedObjectRef.value= { sharedText: value,objectType:'text' }
|
|
||||||
// emit('update:selectedObject', { ...props.selectedObject, sharedText: value,objectType:'text' });
|
|
||||||
}
|
|
||||||
const setValue=(value:string)=>{
|
|
||||||
sharedText.value = value;
|
|
||||||
if(selectedObjectRef.value.sharedText!==value){
|
|
||||||
selectedObjectRef.value= { sharedText: value,objectType:'text' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const optionsRef=ref(props.options);
|
|
||||||
|
|
||||||
return {
|
|
||||||
filter,
|
|
||||||
dialogVisible,
|
|
||||||
currentDialogName,
|
|
||||||
currentComponent,
|
|
||||||
canInputFlag,
|
|
||||||
openDialog,
|
|
||||||
closeDialog,
|
|
||||||
handleSelect,
|
|
||||||
clearSharedText,
|
|
||||||
updateSharedText,
|
|
||||||
setValue,
|
|
||||||
sharedText,
|
|
||||||
inputRef,
|
|
||||||
optionsRef,
|
|
||||||
selectedObjectRef
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<template>
|
|
||||||
<field-list v-model="selected" type="single" :filter="filter" :appId="sourceApp ? sourceApp : appId"
|
|
||||||
:fields="sourceFields" @update:modelValue="handleSelect" />
|
|
||||||
</template>
|
|
||||||
<script lang="ts">
|
|
||||||
import { computed, inject, ref } from 'vue';
|
|
||||||
import FieldList from '../FieldList.vue';
|
|
||||||
export default {
|
|
||||||
name: 'FieldAdd',
|
|
||||||
components: {
|
|
||||||
FieldList,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
appId: Number,
|
|
||||||
filter: String
|
|
||||||
},
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const sourceFields = inject<Array<unknown>>('sourceFields')
|
|
||||||
const sourceApp = inject<number>('sourceApp')
|
|
||||||
const appId = computed(() => {
|
|
||||||
if (sourceFields || sourceApp) {
|
|
||||||
return sourceApp.value
|
|
||||||
} else {
|
|
||||||
return props.appId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
sourceFields,
|
|
||||||
sourceApp,
|
|
||||||
selected: ref([]),
|
|
||||||
handleSelect: (newSelection: any[]) => {
|
|
||||||
|
|
||||||
if (newSelection.length > 0) {
|
|
||||||
const v = newSelection[0]
|
|
||||||
emit('select', { _t: `field(${appId.value},${v.name})`, ...v }); // 假设您只需要选择的第一个字段的名称
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
<template>
|
|
||||||
<variable-list v-model="selected" type="single" :vars="vars" :filter="filter" @update:modelValue="handleSelect" />
|
|
||||||
</template>
|
|
||||||
<script lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import VariableList from '../VariableList.vue';
|
|
||||||
import { useFlowEditorStore } from 'src/stores/flowEditor';
|
|
||||||
import { IActionVariable } from 'src/types/ActionTypes';
|
|
||||||
export default {
|
|
||||||
name: 'VariableAdd',
|
|
||||||
components: {
|
|
||||||
VariableList,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
appId: Number,
|
|
||||||
filter: String
|
|
||||||
},
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const store = useFlowEditorStore();
|
|
||||||
let vars: IActionVariable[] = [];
|
|
||||||
console.log(store.currentFlow !== undefined && store.activeNode !== undefined);
|
|
||||||
|
|
||||||
if (store.currentFlow !== undefined && store.activeNode !== undefined) {
|
|
||||||
vars = store.currentFlow.getVarNames(store.activeNode);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
vars,
|
|
||||||
selected: ref([]),
|
|
||||||
handleSelect: (newSelection: any[]) => {
|
|
||||||
if (newSelection.length > 0) {
|
|
||||||
const v = newSelection[0];
|
|
||||||
let name = v.name
|
|
||||||
if (typeof name === 'object') {
|
|
||||||
name = name.name
|
|
||||||
}
|
|
||||||
emit('select', { _t: `var(${name})`, ...v }); // 假设您只需要选择的第一个字段的名称
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -30,9 +30,9 @@ export default {
|
|||||||
{ name: 'code', label: 'フィールドコード', align: 'left', field: 'code', sortable: true },
|
{ name: 'code', label: 'フィールドコード', align: 'left', field: 'code', sortable: true },
|
||||||
{ name: 'type', label: 'フィールドタイプ', align: 'left', field: 'type', sortable: true }
|
{ name: 'type', label: 'フィールドタイプ', align: 'left', field: 'type', sortable: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
const { state : rows, isReady: isLoaded, isLoading } = useAsyncState((args) => {
|
const { state : rows, isReady: isLoaded, isLoading } = useAsyncState((args) => {
|
||||||
if (props.fields && Object.keys(props.fields).length > 0) {
|
if (props.fields) {
|
||||||
return props.fields.map(f => ({ name: f.label, objectType: 'field', ...f }));
|
return props.fields.map(f => ({ name: f.label, objectType: 'field', ...f }));
|
||||||
} else {
|
} else {
|
||||||
return api.get('api/v1/appfields', {
|
return api.get('api/v1/appfields', {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
import { ref, onMounted, reactive, watchEffect } from 'vue'
|
import { ref, onMounted, reactive, watchEffect } from 'vue'
|
||||||
import { api } from 'boot/axios';
|
import { api } from 'boot/axios';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'fieldSelect',
|
name: 'fieldSelect',
|
||||||
props: {
|
props: {
|
||||||
@@ -29,14 +28,10 @@ export default {
|
|||||||
type:Array,
|
type:Array,
|
||||||
default:()=>[]
|
default:()=>[]
|
||||||
},
|
},
|
||||||
fieldTypes:{
|
updateSelects: {
|
||||||
type:Array,
|
|
||||||
default:()=>[]
|
|
||||||
},
|
|
||||||
filter: String,
|
|
||||||
updateSelectFields: {
|
|
||||||
type: Function
|
type: Function
|
||||||
},
|
},
|
||||||
|
filter: String,
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const isLoaded = ref(false);
|
const isLoaded = ref(false);
|
||||||
@@ -48,39 +43,33 @@ export default {
|
|||||||
const pageSetting = ref({
|
const pageSetting = ref({
|
||||||
sortBy: 'desc',
|
sortBy: 'desc',
|
||||||
descending: false,
|
descending: false,
|
||||||
page: 1,
|
page: 2,
|
||||||
rowsPerPage: props.not_page ? 0 : 5
|
rowsPerPage: props.not_page ? 0 : 5
|
||||||
// rowsNumber: xx if getting data from a server
|
// rowsNumber: xx if getting data from a server
|
||||||
});
|
});
|
||||||
const rows = reactive([]);
|
const rows = reactive([]);
|
||||||
const selected = ref(props.selectedFields && props.selectedFields.length>0?props.selectedFields:[]);
|
const selected = ref(props.selectedFields && props.selectedFields.length>0?props.selectedFields:[]);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
props.updateSelects(selected);
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const url = props.fieldTypes.includes('SPACER')?'api/v1/allfields':'api/v1/appfields';
|
const res = await api.get('api/v1/appfields', {
|
||||||
const res = await api.get(url, {
|
|
||||||
params: {
|
params: {
|
||||||
app: props.appId
|
app: props.appId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let fields = res.data.properties;
|
let fields = res.data.properties;
|
||||||
|
console.log(fields);
|
||||||
Object.keys(fields).forEach((key) => {
|
Object.keys(fields).forEach((key) => {
|
||||||
const fld = fields[key];
|
const fld = fields[key];
|
||||||
if(props.fieldTypes.length===0 || props.fieldTypes.includes(fld.type)){
|
// rows.push({name:fields[key].label,code:fields[key].code,type:fields[key].type});
|
||||||
rows.push({ name: fld.label || fld.code, ...fld });
|
rows.push({ name: fld.label, ...fld });
|
||||||
}else if(props.fieldTypes.includes("lookup") && ("lookup" in fld)){
|
|
||||||
rows.push({ name: fld.label || fld.code, ...fld });
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
isLoaded.value = true;
|
isLoaded.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(()=>{
|
|
||||||
if (selected.value && selected.value[0] && props.updateSelectFields) {
|
|
||||||
props.updateSelectFields(selected)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
columns,
|
columns,
|
||||||
rows,
|
rows,
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- <div class="q-pa-md q-gutter-sm" > -->
|
<!-- <div class="q-pa-md q-gutter-sm" > -->
|
||||||
<q-dialog :model-value="visible" persistent bordered >
|
<q-dialog :model-value="visible" persistent bordered >
|
||||||
<q-card class="" style="min-width: 40vw; max-width: 80vw; max-height: 95vh;" :style="cardStyle">
|
<q-card style="min-width: 40vw; max-width: 80vw; max-height: 95vh;" :style="cardStyle">
|
||||||
<q-toolbar class="bg-grey-4">
|
<q-toolbar class="bg-grey-4">
|
||||||
<q-toolbar-title>{{ name }}</q-toolbar-title>
|
<q-toolbar-title>{{ name }}</q-toolbar-title>
|
||||||
<q-space></q-space>
|
<q-space></q-space>
|
||||||
<slot name="toolbar"></slot>
|
<slot name="toolbar"></slot>
|
||||||
<q-btn flat round dense icon="close" @click="CloseDialogue('Cancel')" />
|
<q-btn flat round dense icon="close" @click="CloseDialogue('Cancel')" />
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
<q-card-section class="q-mt-md" :style="sectionStyle">
|
<q-card-section>
|
||||||
|
<!-- <div class="text-h6">{{ name }}</div> -->
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="q-pt-none" :style="sectionStyle">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</q-card-section>
|
</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('OK')" />
|
||||||
<q-btn flat label="キャンセル" v-close-popup @click="CloseDialogue('Cancel')" />
|
<q-btn flat label="キャンセル" v-close-popup @click="CloseDialogue('Cancel')" />
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="q-pa-md">
|
<div class="q-pa-md">
|
||||||
<q-table flat bordered row-key="id" :selection="type" :selected="modelValue" :filter="filter"
|
<q-table flat bordered row-key="id" :selection="type" :selected="modelValue"
|
||||||
@update:selected="$emit('update:modelValue', $event)" :columns="columns" :rows="rows" />
|
@update:selected="$emit('update:modelValue', $event)" :columns="columns" :rows="rows" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -19,8 +19,7 @@ export default {
|
|||||||
reqired: true,
|
reqired: true,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
modelValue: Array,
|
modelValue: Array
|
||||||
filter: String
|
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
'update:modelValue'
|
'update:modelValue'
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
<q-tree :nodes="store.eventTree.screens" node-key="eventId" children-key="events" no-connectors
|
<q-tree :nodes="store.eventTree.screens" node-key="eventId" children-key="events" no-connectors
|
||||||
v-model:expanded="store.expandedScreen" :dense="true" :ref="tree">
|
v-model:expanded="store.expandedScreen" :dense="true" :ref="tree">
|
||||||
<template v-slot:header-EVENT="prop">
|
<template v-slot:header-EVENT="prop">
|
||||||
<div :ref="prop.node.eventId" class="row col items-center no-wrap event-node" @click="onSelected(prop.node)">
|
<div :ref="prop.node.eventId" class="row col items-center no-wrap event-node">
|
||||||
<q-icon v-if="prop.node.eventId" name="play_circle" :color="prop.node.hasFlow ? 'green' : 'grey'" size="16px"
|
<q-icon v-if="prop.node.eventId" name="play_circle" :color="prop.node.hasFlow ? 'green' : 'grey'" size="16px"
|
||||||
class="q-mr-sm">
|
class="q-mr-sm">
|
||||||
</q-icon>
|
</q-icon>
|
||||||
<div class="no-wrap"
|
<div class="no-wrap" @click="onSelected(prop.node)"
|
||||||
:class="selectedEvent && prop.node.eventId === selectedEvent.eventId ? 'selected-node' : ''">{{
|
:class="selectedEvent && prop.node.eventId === selectedEvent.eventId ? 'selected-node' : ''">{{
|
||||||
prop.node.label }}</div>
|
prop.node.label }}</div>
|
||||||
<q-space></q-space>
|
<q-space></q-space>
|
||||||
@@ -15,21 +15,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:header-CHANGE="prop">
|
<template v-slot:header-CHANGE="prop">
|
||||||
<div class="row col items-start no-wrap event-node">
|
<div class="row col items-center no-wrap event-node">
|
||||||
<div class="no-wrap"
|
<div class="no-wrap">{{ prop.node.label }}</div>
|
||||||
:class="selectedEvent && prop.node.eventId === selectedEvent.eventId ? 'selected-node' : ''"
|
|
||||||
>{{ prop.node.label }}</div>
|
|
||||||
<q-space></q-space>
|
<q-space></q-space>
|
||||||
<q-icon name="add_circle" color="primary" size="16px" class="q-mr-sm"
|
<q-icon name="add_circle" color="primary" size="16px" class="q-mr-sm"
|
||||||
@click="addChangeEvent(prop.node)"></q-icon>
|
@click="addChangeEvent(prop.node)"></q-icon>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:header-DELETABLE="prop">
|
<template v-slot:header-DELETABLE="prop">
|
||||||
<div class="row col items-start event-node" @click="onSelected(prop.node)">
|
<div class="row col items-center event-node">
|
||||||
<q-icon v-if="prop.node.eventId" name="play_circle" :color="prop.node.hasFlow ? 'green' : 'grey'" size="16px" class="q-mr-sm" />
|
<div class="row col items-center" @click="onSelected(prop.node)">
|
||||||
<div class="no-wrap" :class="selectedEvent && prop.node.eventId === selectedEvent.eventId ? 'selected-node' : ''" >{{ prop.node.label }}</div>
|
<q-icon v-if="prop.node.eventId" name="play_circle" :color="prop.node.hasFlow ? 'green' : 'grey'" size="16px"
|
||||||
<q-space></q-space>
|
class="q-mr-sm">
|
||||||
<q-icon name="delete_forever" color="negative" size="16px" @click="deleteEvent(prop.node)"></q-icon>
|
</q-icon>
|
||||||
|
<div>{{ prop.node.label }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<q-btn class="q-mr-sm delete-btn" flat fab-mini icon="delete_forever" padding="none" color="negative"
|
||||||
|
@click="deleteEvent(prop.node)"></q-btn>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</q-tree>
|
</q-tree>
|
||||||
|
|||||||
@@ -48,8 +48,8 @@
|
|||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeAFBox">
|
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeFieldDialog" ref="fieldDlg">
|
||||||
<AppFieldSelectBox v-model:selectedField="selectedField" :selectType="selectType" ref="afBox" :fieldTypes="fieldTypes"/>
|
<AppFieldSelectBox v-model:selectedField="selectedField" :selectType="selectType" />
|
||||||
</show-dialog>
|
</show-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -66,8 +66,7 @@ export interface IApp {
|
|||||||
export interface IField {
|
export interface IField {
|
||||||
name: string,
|
name: string,
|
||||||
code: string,
|
code: string,
|
||||||
type: string,
|
type: string
|
||||||
label?:string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAppFields {
|
export interface IAppFields {
|
||||||
@@ -102,20 +101,15 @@ export default defineComponent({
|
|||||||
selectType: {
|
selectType: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'single'
|
default: 'single'
|
||||||
},
|
|
||||||
fieldTypes:{
|
|
||||||
type:Array,
|
|
||||||
default:()=>[]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const show = ref(false);
|
const show = ref(false);
|
||||||
const afBox = ref();
|
|
||||||
const selectedField = ref<IAppFields>({
|
const selectedField = ref<IAppFields>({
|
||||||
app: undefined,
|
app: undefined,
|
||||||
fields: []
|
fields: []
|
||||||
});
|
});
|
||||||
if (props.modelValue && 'app' in props.modelValue && 'fields' in props.modelValue) {
|
if (props.modelValue && "app" in props.modelValue && "fields" in props.modelValue) {
|
||||||
selectedField.value = props.modelValue as IAppFields;
|
selectedField.value = props.modelValue as IAppFields;
|
||||||
}
|
}
|
||||||
const store = useFlowEditorStore();
|
const store = useFlowEditorStore();
|
||||||
@@ -129,28 +123,17 @@ export default defineComponent({
|
|||||||
const removeField = (index: number) => {
|
const removeField = (index: number) => {
|
||||||
selectedField.value.fields.splice(index, 1);
|
selectedField.value.fields.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeAFBox = (val: string) => {
|
|
||||||
if (val == 'OK') {
|
|
||||||
console.log(afBox.value);
|
|
||||||
|
|
||||||
selectedField.value = afBox.value.selField;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
emit('update:modelValue', selectedField.value);
|
emit('update:modelValue', selectedField.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
store,
|
store,
|
||||||
afBox,
|
|
||||||
show,
|
show,
|
||||||
showDg: () => { show.value = true },
|
showDg: () => { show.value = true },
|
||||||
selectedField,
|
selectedField,
|
||||||
clear,
|
clear,
|
||||||
removeField,
|
removeField,
|
||||||
closeAFBox,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,13 +18,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ConditionNode, ConditionTree, Operator, OperatorListItem } from 'app/src/types/Conditions';
|
import { ConditionNode, ConditionTree, Operator, OperatorListItem } from 'app/src/types/Conditions';
|
||||||
import { computed, defineComponent, provide, reactive, ref, watchEffect } from 'vue';
|
import { computed, defineComponent, provide, reactive, ref, watchEffect } from 'vue';
|
||||||
import ConditionEditor from '../ConditionEditor/ConditionEditor.vue';
|
import ConditionEditor from '../ConditionEditor/ConditionEditor.vue';
|
||||||
import { IActionProperty } from 'src/types/ActionTypes';
|
|
||||||
import { } from 'src/types/';
|
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
props?: {
|
||||||
|
name: string;
|
||||||
|
modelValue?: {
|
||||||
|
app: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
type: string;
|
||||||
|
label: string;
|
||||||
|
code: string;
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -35,7 +51,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
context: {
|
context: {
|
||||||
type: Array<IActionProperty>,
|
type: Array<Props>,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
displayName: {
|
displayName: {
|
||||||
@@ -68,29 +84,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
operatorList: {
|
operatorList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
|
||||||
inputConfig: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
left: {
|
|
||||||
canInput: false,
|
|
||||||
buttonsConfig: [
|
|
||||||
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
|
|
||||||
{ label: '変数', color: 'green', type: 'VariableAdd' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
right: {
|
|
||||||
canInput: true,
|
|
||||||
buttonsConfig: [
|
|
||||||
{ label: '変数', color: 'green', type: 'VariableAdd' },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const source = props.context.find(element => element.props.name === 'sources')
|
const source = props.context.find(element => element?.props?.name === 'sources')
|
||||||
|
|
||||||
if (source) {
|
if (source) {
|
||||||
if (props.sourceType === 'field') {
|
if (props.sourceType === 'field') {
|
||||||
@@ -100,8 +98,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provide('leftDynamicItemConfig', props.inputConfig.left);
|
|
||||||
provide('rightDynamicItemConfig', props.inputConfig.right);
|
|
||||||
provide('Operator', props.operatorList);
|
provide('Operator', props.operatorList);
|
||||||
|
|
||||||
const btnDisable = computed(() => {
|
const btnDisable = computed(() => {
|
||||||
|
|||||||
@@ -16,61 +16,44 @@
|
|||||||
</q-card>
|
</q-card>
|
||||||
</template>
|
</template>
|
||||||
</q-field>
|
</q-field>
|
||||||
<show-dialog v-model:visible="dgIsShow" name="データマッピング" @close="closeDg" min-width="55vw" min-height="60vh">
|
<show-dialog v-model:visible="dgIsShow" name="データマッピング" @close="closeDg" min-width="50vw" min-height="60vh">
|
||||||
|
|
||||||
<div class="">
|
<div class="q-mx-md">
|
||||||
<div class="row q-col-gutter-x-xs flex-center">
|
<div class="row q-col-gutter-x-xs flex-center">
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<div class="q-mx-xs">ソース</div>
|
<div class="q-mx-xs">From</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-1">
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="col-1">
|
|
||||||
</div> -->
|
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<div class="row justify-between q-mr-md">
|
<div class="q-mx-xs">To</div>
|
||||||
<div class="">{{ sourceApp?.name }}</div>
|
|
||||||
<q-btn outline color="primary" size="xs" label="最新のフィールドを取得する"
|
|
||||||
@click="() => updateFields(sourceAppId!)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1 q-pl-sm">
|
<div class="col-1"><q-btn flat round dense icon="add" size="sm" @click="addMappingObject" />
|
||||||
キー
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-virtual-scroll style="max-height: 60vh;" :items="mappingProps" separator v-slot="{ item, index }">
|
<div class="q-my-sm" v-for="(item, index) in mappingProps" :key="item.id">
|
||||||
<!-- <div class="q-my-sm" v-for="(item, index) in mappingProps" :key="item.id"> -->
|
<div class="row q-col-gutter-x-xs flex-center">
|
||||||
<div class="row q-pa-sm q-col-gutter-x-md flex-center">
|
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<ConditionObject :config="config" v-model="item.from" :disabled="item.disabled"
|
<ConditionObject v-model="item.from" />
|
||||||
:label="item.disabled ? '「Lookup」によってロックされる' : undefined" />
|
</div>
|
||||||
|
<div class="col-1">
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="col-1">
|
|
||||||
</div> -->
|
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<q-field v-model="item.vName" type="text" outlined dense :disable="item.disabled" >
|
<q-field v-model="item.vName" type="text" outlined dense>
|
||||||
<!-- <template v-slot:append>
|
<template v-slot:append>
|
||||||
<q-icon name="search" class="cursor-pointer"
|
<q-icon name="search" class="cursor-pointer"
|
||||||
@click="() => { mappingProps[index].to.isDialogVisible = true }" />
|
@click="() => { mappingProps[index].to.isDialogVisible = true }" />
|
||||||
</template> -->
|
</template>
|
||||||
<template v-slot:control>
|
<template v-slot:control>
|
||||||
<div class="self-center full-width no-outline" tabindex="0"
|
<div class="self-center full-width no-outline" tabindex="0"
|
||||||
v-if="item.to.app?.name && item.to.fields?.length > 0 && item.to.fields[0].label">
|
v-if="item.to.app?.name && item.to.fields?.length > 0 && item.to.fields[0].label">
|
||||||
{{ `${item.to.fields[0].label}` }}
|
{{ `${item.to.app?.name} : ${item.to.fields[0].label}` }}
|
||||||
<span class="text-red" v-if="item.to.fields[0].required">*</span>
|
|
||||||
<q-tooltip class="bg-yellow-2 text-black shadow-4" >
|
|
||||||
<div>アプリ : {{ item.to.app.name }}</div>
|
|
||||||
<div>フィールドのコード : {{ item.to.fields[0].code }}</div>
|
|
||||||
<div>フィールドのタイプ : {{ item.to.fields[0].type }}</div>
|
|
||||||
<div v-if="item.to.fields[0].required">必須項目</div>
|
|
||||||
<!-- <div>フィールド : {{ item.to.fields[0] }}</div>
|
|
||||||
<div>フィールド : {{ item.isKey }}</div> -->
|
|
||||||
</q-tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</q-field>
|
</q-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<q-checkbox size="sm" v-model="item.isKey" :disable="item.disabled" />
|
<q-btn flat round dense icon="delete" size="sm" @click="() => deleteMappingObject(index)" />
|
||||||
<!-- <q-btn flat round dense icon="delete" size="sm" @click="() => deleteMappingObject(index)" /> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -82,11 +65,6 @@
|
|||||||
</FieldSelect>
|
</FieldSelect>
|
||||||
<AppFieldSelectBox v-else v-model:selectedField="mappingProps[index].to" />
|
<AppFieldSelectBox v-else v-model:selectedField="mappingProps[index].to" />
|
||||||
</show-dialog>
|
</show-dialog>
|
||||||
<!-- </div> -->
|
|
||||||
</q-virtual-scroll>
|
|
||||||
|
|
||||||
<div class="q-mt-lg q-ml-md row ">
|
|
||||||
<q-checkbox size="sm" v-model="createWithNull" label="キーが存在しない場合は新規に作成され、存在する場合はデータが更新されます。" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</show-dialog>
|
</show-dialog>
|
||||||
@@ -95,15 +73,14 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { computed, defineComponent, watch, isRef, reactive, ref, watchEffect } from 'vue';
|
import { computed, defineComponent, reactive, ref, watchEffect } from 'vue';
|
||||||
import ConditionObject from '../ConditionEditor/ConditionObject.vue';
|
import ConditionObject from '../ConditionEditor/ConditionObject.vue';
|
||||||
import ShowDialog from '../ShowDialog.vue';
|
import ShowDialog from '../ShowDialog.vue';
|
||||||
import AppFieldSelectBox from '../AppFieldSelectBox.vue';
|
import AppFieldSelectBox from '../AppFieldSelectBox.vue';
|
||||||
import FieldSelect from '../FieldSelect.vue';
|
import FieldSelect from '../FieldSelect.vue';
|
||||||
import { IApp, IField } from './AppFieldSelect.vue';
|
import IAppFields from './AppFieldSelect.vue';
|
||||||
import { api } from 'boot/axios';
|
|
||||||
|
|
||||||
type ContextProps = {
|
type Props = {
|
||||||
props?: {
|
props?: {
|
||||||
name: string;
|
name: string;
|
||||||
modelValue?: {
|
modelValue?: {
|
||||||
@@ -114,25 +91,15 @@ type ContextProps = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
type ValueType = {
|
||||||
type CurrentModelValueType = {
|
|
||||||
data: MappingValueType[];
|
|
||||||
createWithNull: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
type MappingValueType = {
|
|
||||||
id: string;
|
id: string;
|
||||||
from: { sharedText?: string };
|
from: object;
|
||||||
to: {
|
to: typeof IAppFields & {
|
||||||
app?: IApp,
|
|
||||||
fields: IField[],
|
|
||||||
isDialogVisible: boolean;
|
isDialogVisible: boolean;
|
||||||
};
|
};
|
||||||
isKey: boolean;
|
|
||||||
disabled: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const blackListLabelName = ['レコード番号', '作業者', '更新者', '更新日時', '作成日時', '作成者']
|
const defaultMappingProp = () => ({ id: uuidv4(), to: { app: {}, fields: [], isDialogVisible: false } });
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'DataMapping',
|
name: 'DataMapping',
|
||||||
@@ -145,7 +112,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
context: {
|
context: {
|
||||||
type: Array<ContextProps>,
|
type: Array<Props>,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
displayName: {
|
displayName: {
|
||||||
@@ -157,7 +124,7 @@ export default defineComponent({
|
|||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object as () => CurrentModelValueType,
|
type: Object as () => ValueType[],
|
||||||
},
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -166,94 +133,55 @@ export default defineComponent({
|
|||||||
onlySourceSelect: {
|
onlySourceSelect: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
}
|
||||||
fieldTypes:{
|
|
||||||
type:Array,
|
|
||||||
default:()=>[]
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
const closeDg = () => {
|
||||||
|
emit('update:modelValue', mappingProps
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeToDg = () => {
|
||||||
|
emit('update:modelValue', mappingProps
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappingProps: ValueType[] = props.modelValue
|
||||||
|
? props.modelValue
|
||||||
|
: reactive([defaultMappingProp()]);
|
||||||
|
|
||||||
|
|
||||||
|
const deleteMappingObject = (index: number) => mappingProps.length === 1
|
||||||
|
? mappingProps.splice(0, mappingProps.length, defaultMappingProp())
|
||||||
|
: mappingProps.splice(index, 1);
|
||||||
|
|
||||||
|
const mappingObjectsInputDisplay = computed(() =>
|
||||||
|
mappingProps ?
|
||||||
|
mappingProps
|
||||||
|
.filter(item => item.from?.name && item.to.fields?.length > 0)
|
||||||
|
.map(item => {
|
||||||
|
const name = typeof item.from?.name === 'string'
|
||||||
|
? item.from.name
|
||||||
|
: item.from?.name.name;
|
||||||
|
return `[${name}] - (${item.to.app?.name} : ${item.to.fields[0].label})`;
|
||||||
|
})
|
||||||
|
: []
|
||||||
|
);
|
||||||
|
|
||||||
const source = props.context.find(element => element?.props?.name === 'sources')
|
const source = props.context.find(element => element?.props?.name === 'sources')
|
||||||
|
|
||||||
const sourceApp = computed(() => source?.props?.modelValue?.app);
|
const sourceApp = computed(() => source?.props?.modelValue?.app);
|
||||||
|
|
||||||
const sourceAppId = computed(() => sourceApp.value?.id);
|
const sourceAppId = computed(() => sourceApp.value?.id);
|
||||||
|
|
||||||
const closeDg = () => {
|
|
||||||
emit('update:modelValue', { data: mappingProps.value, createWithNull: createWithNull.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeToDg = () => {
|
|
||||||
emit('update:modelValue', { data: mappingProps.value, createWithNull: createWithNull.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
const mappingProps = ref(props.modelValue?.data ?? []);
|
|
||||||
|
|
||||||
const createWithNull = ref(props.modelValue?.createWithNull ?? false)
|
|
||||||
|
|
||||||
// 外部ソースコンポーネントの appid をリッスンし、変更されたときに現在のコンポーネントを更新します
|
|
||||||
watch(() => sourceAppId.value, async (newId,) => {
|
|
||||||
if (!newId) return;
|
|
||||||
updateFields(newId)
|
|
||||||
})
|
|
||||||
|
|
||||||
const updateFields = async (sourceAppId: string) => {
|
|
||||||
const ktAppFields = await api.get('api/v1/appfields', {
|
|
||||||
params: {
|
|
||||||
app: sourceAppId
|
|
||||||
}
|
|
||||||
}).then(res => {
|
|
||||||
return Object.values(res.data.properties)
|
|
||||||
// kintoneのデフォルトの非表示フィールドフィルタリング
|
|
||||||
.filter(f => !blackListLabelName.find(label => f.label === label))
|
|
||||||
.map(f => ({ name: f.label, objectType: 'field', ...f }))
|
|
||||||
.map(f => {
|
|
||||||
// 更新前の値を求める
|
|
||||||
const beforeData = mappingProps.value.find(m => m.to.fields[0].code === f.code)
|
|
||||||
return {
|
|
||||||
id: uuidv4(),
|
|
||||||
from: beforeData?.from ?? {}, // 以前のデータを入力します
|
|
||||||
to: {
|
|
||||||
app: sourceApp.value,
|
|
||||||
fields: [f],
|
|
||||||
isDialogVisible: false
|
|
||||||
},
|
|
||||||
isKey: beforeData?.isKey ?? false, // 以前のデータを入力します
|
|
||||||
disabled: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// 「ルックアップ」によってロックされているフィールドを検索する
|
|
||||||
const lookupFixedField = ktAppFields
|
|
||||||
.filter(field => field.to.fields[0].lookup !== undefined)
|
|
||||||
.flatMap(field => field.to.fields[0].lookup.fieldMappings.map((m) => m.field))
|
|
||||||
|
|
||||||
// 「ルックアップ」でロックされたビューコンポーネントを非対話型に設定します
|
|
||||||
if (lookupFixedField.length > 0) {
|
|
||||||
ktAppFields.filter(f => lookupFixedField.includes(f.to.fields[0].code)).forEach(f => f.disabled = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
mappingProps.value = ktAppFields
|
|
||||||
}
|
|
||||||
|
|
||||||
const mappingObjectsInputDisplay = computed(() =>
|
|
||||||
(mappingProps.value && Array.isArray(mappingProps.value)) ?
|
|
||||||
mappingProps.value
|
|
||||||
.filter(item => item.from?.sharedText && item.to.fields?.length > 0)
|
|
||||||
.map(item => {
|
|
||||||
return `field(${item.to.app?.id},${item.to.fields[0].label}) = ${item.from.sharedText} `;
|
|
||||||
})
|
|
||||||
: []
|
|
||||||
);
|
|
||||||
|
|
||||||
const btnDisable = computed(() => props.onlySourceSelect ? !(source?.props?.modelValue?.app?.id) : false);
|
const btnDisable = computed(() => props.onlySourceSelect ? !(source?.props?.modelValue?.app?.id) : false);
|
||||||
|
|
||||||
watchEffect(() => {
|
//集計処理方法
|
||||||
emit('update:modelValue', { data: mappingProps.value, createWithNull: createWithNull.value });
|
|
||||||
});
|
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
emit('update:modelValue', mappingProps);
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
uuidv4,
|
uuidv4,
|
||||||
dgIsShow: ref(false),
|
dgIsShow: ref(false),
|
||||||
@@ -261,24 +189,14 @@ export default defineComponent({
|
|||||||
toDgIsShow: ref(false),
|
toDgIsShow: ref(false),
|
||||||
closeToDg,
|
closeToDg,
|
||||||
mappingProps,
|
mappingProps,
|
||||||
createWithNull,
|
addMappingObject: () => mappingProps.push(defaultMappingProp()),
|
||||||
updateFields,
|
deleteMappingObject,
|
||||||
// addMappingObject: () => mappingProps.push(defaultMappingProp()),
|
|
||||||
// deleteMappingObject,
|
|
||||||
mappingObjectsInputDisplay,
|
mappingObjectsInputDisplay,
|
||||||
sourceApp,
|
sourceApp,
|
||||||
sourceAppId,
|
sourceAppId,
|
||||||
btnDisable,
|
btnDisable
|
||||||
config: {
|
|
||||||
canInput: false,
|
|
||||||
buttonsConfig: [
|
|
||||||
{ label: 'フィールド', color: 'primary', type: 'FieldAdd' },
|
|
||||||
{ label: '変数', color: 'green', type: 'VariableAdd', editable: false },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss"></style>
|
<style lang="scss"></style>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<ConditionObject v-model="item.field" />
|
<ConditionObject v-model="item.field" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2 q-pa-sm">
|
<div class="col-2">
|
||||||
<q-select v-model="item.logicalOperator" :options="logicalOperators" outlined dense></q-select>
|
<q-select v-model="item.logicalOperator" :options="logicalOperators" outlined dense></q-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
@@ -78,8 +78,14 @@ type Props = {
|
|||||||
|
|
||||||
type ProcessingObjectType = {
|
type ProcessingObjectType = {
|
||||||
field?: {
|
field?: {
|
||||||
sharedText: string;
|
name: string | {
|
||||||
objectType: 'field';
|
name: string;
|
||||||
|
};
|
||||||
|
objectType: string;
|
||||||
|
type: string;
|
||||||
|
code: string;
|
||||||
|
label: string;
|
||||||
|
noLabel: boolean;
|
||||||
};
|
};
|
||||||
logicalOperator?: string;
|
logicalOperator?: string;
|
||||||
vName?: string;
|
vName?: string;
|
||||||
@@ -133,60 +139,44 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}));
|
}));
|
||||||
provide('sourceApp', computed(() => source.props?.modelValue?.app?.id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionName = props.context.find(element => element?.props?.name === 'displayName')
|
const actionName = props.context.find(element => element?.props?.name === 'displayName')
|
||||||
|
|
||||||
const processingProps: ValueType = props.modelValue && props.modelValue.vars
|
const processingProps: ValueType = props.modelValue && props.modelValue.vars
|
||||||
? reactive(props.modelValue)
|
? props.modelValue
|
||||||
: reactive({
|
: reactive({
|
||||||
name: '',
|
name: '',
|
||||||
actionName: actionName?.props?.modelValue as string,
|
actionName: actionName?.props?.modelValue as string,
|
||||||
displayName: '結果(戻り値)',
|
displayName: '結果(戻り値)',
|
||||||
vars: [
|
vars: [{ id: uuidv4() }]
|
||||||
{
|
|
||||||
id: uuidv4(),
|
|
||||||
field:{
|
|
||||||
objectType:'field',
|
|
||||||
sharedText:''
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const closeDg = () => {
|
const closeDg = () => {
|
||||||
emit('update:modelValue', processingProps);
|
emit('update:modelValue', processingProps
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const processingObjects = processingProps.vars;
|
const processingObjects = processingProps.vars;
|
||||||
|
|
||||||
const deleteProcessingObject = (index: number) => {
|
const deleteProcessingObject = (index: number) => processingObjects.length === 1
|
||||||
if(processingObjects.length >0){
|
? processingObjects.splice(0, processingObjects.length, { id: uuidv4() })
|
||||||
processingObjects.splice(index, 1);
|
: processingObjects.splice(index, 1);
|
||||||
}
|
|
||||||
if(processingObjects.length===0){
|
|
||||||
addProcessingObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const processingObjectsInputDisplay = computed(() =>
|
const processingObjectsInputDisplay = computed(() =>
|
||||||
processingObjects ?
|
processingObjects ?
|
||||||
processingObjects
|
processingObjects
|
||||||
.filter(item => item.field && item.logicalOperator && item.vName)
|
.filter(item => item.field && item.logicalOperator && item.vName)
|
||||||
.map(item => {
|
.map(item => {
|
||||||
return`var(${processingProps.name}.${item.vName}) = ${item.field?.sharedText}`
|
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 addProcessingObject=()=>{
|
|
||||||
processingObjects.push({
|
|
||||||
id: uuidv4(),
|
|
||||||
field:{
|
|
||||||
objectType:'field',
|
|
||||||
sharedText:''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//集計処理方法
|
//集計処理方法
|
||||||
const logicalOperators = ref([
|
const logicalOperators = ref([
|
||||||
{
|
{
|
||||||
@@ -228,7 +218,7 @@ export default defineComponent({
|
|||||||
closeDg,
|
closeDg,
|
||||||
processingObjects,
|
processingObjects,
|
||||||
processingProps,
|
processingProps,
|
||||||
addProcessingObject,
|
addProcessingObject: () => processingObjects.push({ id: uuidv4() }),
|
||||||
deleteProcessingObject,
|
deleteProcessingObject,
|
||||||
logicalOperators,
|
logicalOperators,
|
||||||
processingObjectsInputDisplay,
|
processingObjectsInputDisplay,
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ export default defineComponent({
|
|||||||
if(store.eventTree.findEventById(addEventId)){
|
if(store.eventTree.findEventById(addEventId)){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
customEvents.events.push({
|
customEvents.events.push(
|
||||||
eventId: addEventId,
|
new kintoneEvent(
|
||||||
label: displayName,
|
displayName,
|
||||||
parentId: customButtonId,
|
addEventId,
|
||||||
header: 'DELETABLE'
|
customButtonId)
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
{{ selectedField.name }}
|
{{ selectedField.name }}
|
||||||
</q-chip>
|
</q-chip>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- <template v-slot:hint v-if="isSelected">
|
||||||
|
<div> 項目コード:<q-chip size="sm" outline color="secondary" text-color="white">{{selectedField.code}}</q-chip></div>
|
||||||
|
</template> -->
|
||||||
<template v-slot:hint v-if="!isSelected">
|
<template v-slot:hint v-if="!isSelected">
|
||||||
{{ placeholder }}
|
{{ placeholder }}
|
||||||
</template>
|
</template>
|
||||||
@@ -15,15 +18,8 @@
|
|||||||
<q-icon name="search" class="cursor-pointer" color="primary" @click="showDg" />
|
<q-icon name="search" class="cursor-pointer" color="primary" @click="showDg" />
|
||||||
</template>
|
</template>
|
||||||
</q-field>
|
</q-field>
|
||||||
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeDg" min-width="400px">
|
<show-dialog v-model:visible="show" name="フィールド一覧" @close="closeDg" widht="400px">
|
||||||
<template v-slot:toolbar>
|
<field-select ref="appDg" name="フィールド" type="single" :appId="store.appInfo?.appId"></field-select>
|
||||||
<q-input dense debounce="300" v-model="filter" placeholder="検索" clearable>
|
|
||||||
<template v-slot:before>
|
|
||||||
<q-icon name="search" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
</template>
|
|
||||||
<field-select ref="appDg" name="フィールド" :type="selectType" :appId="store.appInfo?.appId" :fieldTypes="fieldTypes" :filter="filter"></field-select>
|
|
||||||
</show-dialog>
|
</show-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -58,14 +54,6 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
selectType:{
|
|
||||||
type:String,
|
|
||||||
default:'single'
|
|
||||||
},
|
|
||||||
fieldTypes:{
|
|
||||||
type:Array,
|
|
||||||
default:()=>[]
|
|
||||||
},
|
|
||||||
hint: {
|
hint: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
@@ -106,8 +94,7 @@ export default defineComponent({
|
|||||||
showDg,
|
showDg,
|
||||||
closeDg,
|
closeDg,
|
||||||
selectedField,
|
selectedField,
|
||||||
isSelected,
|
isSelected
|
||||||
filter:ref(''),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,10 +33,6 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
fieldTypes:{
|
|
||||||
type:Array,
|
|
||||||
default:()=>[]
|
|
||||||
},
|
|
||||||
hint: {
|
hint: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default defineComponent({
|
|||||||
default:'',
|
default:'',
|
||||||
},
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [Array,String],
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -113,19 +113,19 @@ export const useFlowEditorStore = defineStore('flowEditor', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
async deleteEvent(event: IKintoneEvent) {
|
deleteEvent(event: IKintoneEvent) {
|
||||||
const store = useFlowEditorStore();
|
const store = useFlowEditorStore();
|
||||||
if (event.flowData) {
|
if (event.flowData) {
|
||||||
const flow = event.flowData;
|
const flow = event.flowData;
|
||||||
if (flow.id !== '') {
|
if (flow.id === '') {
|
||||||
await flowCtrl.DeleteFlow(flow.id)
|
return;
|
||||||
if (this.flows) {
|
|
||||||
this.flows = this.flows.filter((f) => f.id !== flow.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
flowCtrl.DeleteFlow(flow.id)
|
||||||
eventTree.deleteEvent(event, store);
|
eventTree.deleteEvent(event, store);
|
||||||
}
|
if(this.flows){
|
||||||
else {
|
this.flows = this.flows.filter((f) => f.id !== flow.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
eventTree.deleteEvent(event, store);
|
eventTree.deleteEvent(event, store);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
|
|
||||||
export interface IApp {
|
|
||||||
id: string,
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IField {
|
|
||||||
label?:string;
|
|
||||||
code:string;
|
|
||||||
type?:string;
|
|
||||||
required?:boolean;
|
|
||||||
options?:string;
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 選択されたフィールド
|
|
||||||
*/
|
|
||||||
export interface ISelectedField extends IField{
|
|
||||||
objectType:'Field'|'RefField';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAppFields {
|
|
||||||
app?: IApp,
|
|
||||||
name?:string;
|
|
||||||
fields: IField[]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 条件式の入力ボタンの属性定義
|
|
||||||
*/
|
|
||||||
export interface IButtonConfig{
|
|
||||||
label: string;
|
|
||||||
color: string;
|
|
||||||
type: 'FieldAdd' | 'VariableAdd' | 'FunctionAdd';
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 条件入力項目の属性
|
|
||||||
*/
|
|
||||||
export interface IDynamicInputConfig{
|
|
||||||
canInput: boolean;
|
|
||||||
buttonsConfig: IButtonConfig[];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 条件式入力項目の属性
|
|
||||||
*/
|
|
||||||
export interface ICoditionConfig{
|
|
||||||
left:IDynamicInputConfig,
|
|
||||||
right:IDynamicInputConfig
|
|
||||||
}
|
|
||||||
@@ -201,12 +201,11 @@ export class ConditionTree {
|
|||||||
} else {
|
} else {
|
||||||
const condNode=node as ConditionNode;
|
const condNode=node as ConditionNode;
|
||||||
if (condNode.object && condNode.operator ) {
|
if (condNode.object && condNode.operator ) {
|
||||||
// let value=condNode.value;
|
let value=condNode.value;
|
||||||
// if(value && typeof value ==='object' && ('label' in value)){
|
if(value && typeof value ==='object' && ('label' in value)){
|
||||||
// value =condNode.value.label;
|
value =condNode.value.label;
|
||||||
// }
|
}
|
||||||
return `${condNode.object.sharedText} ${typeof condNode.operator === 'object' ? condNode.operator.label : condNode.operator} ${condNode.value.sharedText}`;
|
return `${typeof condNode.object.name === 'object' ? condNode.object.name.name : condNode.object.name} ${typeof condNode.operator === 'object' ? condNode.operator.label : condNode.operator} '${value}'`;
|
||||||
// return `${typeof condNode.object.name === 'object' ? condNode.object.name.name : condNode.object.name} ${typeof condNode.operator === 'object' ? condNode.operator.label : condNode.operator} '${value}'`;
|
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -238,10 +237,11 @@ export class ConditionTree {
|
|||||||
} else {
|
} else {
|
||||||
const condNode=node as ConditionNode;
|
const condNode=node as ConditionNode;
|
||||||
if (condNode.object && condNode.operator ) {
|
if (condNode.object && condNode.operator ) {
|
||||||
if (!condNode.object.code || !condNode.value.sharedText){
|
let value=condNode.value;
|
||||||
return '';
|
if(value && typeof value ==='object' && ('label' in value)){
|
||||||
}
|
value =condNode.value.label;
|
||||||
return `${condNode.object.code} ${typeof condNode.operator === 'object' ? condNode.operator.value : condNode.operator} "${condNode.value.sharedText}"`;
|
}
|
||||||
|
return `${condNode.object.code} ${typeof condNode.operator === 'object' ? condNode.operator.value : condNode.operator} "${value}"`;
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,10 @@ export class KintoneEventManager {
|
|||||||
const lastIndex = eventId.lastIndexOf('.');
|
const lastIndex = eventId.lastIndexOf('.');
|
||||||
const groupId = eventId.substring(0, lastIndex);
|
const groupId = eventId.substring(0, lastIndex);
|
||||||
const eventNode = this.findEventById(groupId);
|
const eventNode = this.findEventById(groupId);
|
||||||
if (eventNode && (eventNode.header === 'EVENTGROUP' || eventNode.header === 'CHANGE')) {
|
if (
|
||||||
|
eventNode &&
|
||||||
|
(eventNode.header === 'EVENTGROUP' || eventNode.header === 'CHANGE')
|
||||||
|
) {
|
||||||
const groupEvent = eventNode as kintoneEventGroup;
|
const groupEvent = eventNode as kintoneEventGroup;
|
||||||
|
|
||||||
const newEvent = {
|
const newEvent = {
|
||||||
|
|||||||
6
node_modules/.package-lock.json
generated
vendored
6
node_modules/.package-lock.json
generated
vendored
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "App Builder for kintone",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {}
|
|
||||||
}
|
|
||||||
15
node_modules/.yarn-integrity
generated
vendored
15
node_modules/.yarn-integrity
generated
vendored
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"systemParams": "win32-x64-108",
|
|
||||||
"modulesFolders": [
|
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
"flags": [],
|
|
||||||
"linkedModules": [
|
|
||||||
"@quasar\\quasar-ui-qactivity",
|
|
||||||
"docs"
|
|
||||||
],
|
|
||||||
"topLevelPatterns": [],
|
|
||||||
"lockfileEntries": {},
|
|
||||||
"files": [],
|
|
||||||
"artifacts": {}
|
|
||||||
}
|
|
||||||
16
plugin/kintone-addins/package-lock.json
generated
16
plugin/kintone-addins/package-lock.json
generated
@@ -8,8 +8,7 @@
|
|||||||
"name": "kintone-addins",
|
"name": "kintone-addins",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1"
|
||||||
"yarn": "^1.22.22"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jquery": "^3.5.24",
|
"@types/jquery": "^3.5.24",
|
||||||
@@ -796,19 +795,6 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/yarn": {
|
|
||||||
"version": "1.22.22",
|
|
||||||
"resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz",
|
|
||||||
"integrity": "sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"bin": {
|
|
||||||
"yarn": "bin/yarn.js",
|
|
||||||
"yarnpkg": "bin/yarn.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,14 @@
|
|||||||
"watch": "vite build --watch --mode dev",
|
"watch": "vite build --watch --mode dev",
|
||||||
"server": "vite dev --mode dev",
|
"server": "vite dev --mode dev",
|
||||||
"ngrok": "ngrok http 4173",
|
"ngrok": "ngrok http 4173",
|
||||||
"build": "run-s b:production copyjs:windows copycss:windows",
|
"build": "run-s b:production copy:windows",
|
||||||
"build:dev": "run-s b:dev copyjs:windows copycss:windows",
|
"build:dev": "run-s b:dev copy:windows",
|
||||||
"build:linux": "run-s b:production copyjs:linux copycss:linux",
|
"build:linux": "run-s b:production copy:linux",
|
||||||
"build:linux-dev": "run-s b:dev copy:linux",
|
"build:linux-dev": "run-s b:dev copy:linux",
|
||||||
"b:production": "tsc & vite build --mode production",
|
"b:production": "vite build --mode production",
|
||||||
"b:dev": "tsc & vite build --mode dev",
|
"b:dev": "vite build --mode dev",
|
||||||
"copyjs:windows": "xcopy dist\\*.js ..\\..\\backend\\Temp\\ /E /I /Y",
|
"copy:windows": "xcopy dist\\*.js ..\\..\\backend\\Temp\\ /E /I /Y",
|
||||||
"copyjs:linux": "cp -ur dist/*.js ../../backend/Temp",
|
"copy:linux": "cp -ur dist/*.js ../../backend/Temp"
|
||||||
"copycss:windows": "xcopy dist\\*.css ..\\..\\backend\\Temp\\ /E /I /Y",
|
|
||||||
"copycss:linux": "cp -ur dist/*.css ../../backend/Temp"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jquery": "^3.5.24",
|
"@types/jquery": "^3.5.24",
|
||||||
@@ -26,13 +24,10 @@
|
|||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^4.4.5",
|
"vite": "^4.4.5",
|
||||||
"vite-plugin-checker": "^0.6.4"
|
"vite-plugin-checker": "^0.6.4",
|
||||||
|
"vite-plugin-lib-inject-css": "^2.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kintone/rest-api-client": "^5.5.2",
|
|
||||||
"@popperjs/core": "^2.11.8",
|
|
||||||
"@types/bootstrap": "^5.2.10",
|
|
||||||
"bootstrap": "^5.3.3",
|
|
||||||
"jquery": "^3.7.1"
|
"jquery": "^3.7.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
@import 'bootstrap/scss/bootstrap';
|
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
import {
|
|
||||||
IAction,
|
|
||||||
IActionResult,
|
|
||||||
IActionNode,
|
|
||||||
IActionProperty,
|
|
||||||
IContext,
|
|
||||||
IField,
|
|
||||||
} from "../types/ActionTypes";
|
|
||||||
|
|
||||||
import { actionAddins } from ".";
|
|
||||||
import type { Record} from "@kintone/rest-api-client/lib/src/client/types";
|
|
||||||
import { KintoneRestAPIClient} from "@kintone/rest-api-client";
|
|
||||||
import "./auto-lookup.scss";
|
|
||||||
import "bootstrap/js/dist/modal";
|
|
||||||
// import "bootstrap/js/dist/spinner";
|
|
||||||
import {Modal} from "bootstrap"
|
|
||||||
import $ from "jquery";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
displayName: string;
|
|
||||||
lookupField: LookupField;
|
|
||||||
condition: Condition;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Condition {
|
|
||||||
queryString: string;
|
|
||||||
index: number;
|
|
||||||
type: string;
|
|
||||||
children: Child[];
|
|
||||||
parent: null;
|
|
||||||
logicalOperator: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Child {
|
|
||||||
index: number;
|
|
||||||
type: string;
|
|
||||||
parent: string;
|
|
||||||
object: any;
|
|
||||||
operator: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LookupField {
|
|
||||||
app: App;
|
|
||||||
fields: Field[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Field {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
code: string;
|
|
||||||
label: string;
|
|
||||||
noLabel: boolean;
|
|
||||||
required: boolean;
|
|
||||||
lookup: Lookup;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Lookup {
|
|
||||||
relatedApp: RelatedApp;
|
|
||||||
relatedKeyField: string;
|
|
||||||
fieldMappings: FieldMapping[];
|
|
||||||
lookupPickerFields: any[];
|
|
||||||
filterCond: string;
|
|
||||||
sort: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldMapping {
|
|
||||||
field: string;
|
|
||||||
relatedField: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RelatedApp {
|
|
||||||
app: string;
|
|
||||||
code: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface App {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
createdate: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AutoLookUpAction implements IAction {
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props: Props;
|
|
||||||
constructor() {
|
|
||||||
this.name = "ルックアップ更新";
|
|
||||||
this.actionProps = [];
|
|
||||||
this.props = {} as Props;
|
|
||||||
this.register();
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
* アクセスのメインの処理関数
|
|
||||||
*/
|
|
||||||
async process(
|
|
||||||
prop: IActionNode,
|
|
||||||
event: any,
|
|
||||||
context: IContext
|
|
||||||
): Promise<IActionResult> {
|
|
||||||
this.actionProps = prop.actionProps;
|
|
||||||
this.props = {
|
|
||||||
...prop.ActionValue,
|
|
||||||
condition: JSON.parse((prop.ActionValue as any).condition),
|
|
||||||
} as Props;
|
|
||||||
// console.log(context);
|
|
||||||
|
|
||||||
let result = {
|
|
||||||
canNext: true,
|
|
||||||
result: "",
|
|
||||||
} as IActionResult;
|
|
||||||
try {
|
|
||||||
const lookUpFields = this.props.lookupField.fields.filter(
|
|
||||||
(f) => f.lookup && f.lookup.relatedApp.app === String(kintone.app.getId())
|
|
||||||
);
|
|
||||||
if (!lookUpFields || lookUpFields.length===0) {
|
|
||||||
throw new Error(
|
|
||||||
`ルックアップの設定は不正です。${this.props.lookupField.fields[0].label} `
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const lookUpField = this.props.lookupField.fields[0];
|
|
||||||
const key = event.record[lookUpField.lookup.relatedKeyField].value;
|
|
||||||
const targetRecords = await this.getUpdateRecords(lookUpField, key);
|
|
||||||
//更新対象がない時にスキップ
|
|
||||||
if(targetRecords.length===0){
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
const updateRecords = this.convertForLookup(targetRecords,lookUpField,key);
|
|
||||||
console.log("updateRecords", updateRecords);
|
|
||||||
this.showSpinnerModel(this.props.lookupField.app);
|
|
||||||
await this.updateLookupTarget(updateRecords);
|
|
||||||
this.showResult(this.props.lookupField.app,updateRecords.length);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("ルックアップ更新中例外が発生しました。", error);
|
|
||||||
if(error instanceof Error){
|
|
||||||
event.error = error.message;
|
|
||||||
}else{
|
|
||||||
event.error = "ルックアップ更新中例外が発生しました。";
|
|
||||||
}
|
|
||||||
result.canNext = false;
|
|
||||||
}
|
|
||||||
console.log("autoLookupProps", this.props);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* REST API用クエリ作成
|
|
||||||
* TODO:共通関数として作成
|
|
||||||
* @param lookUpField
|
|
||||||
* @param key
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
makeQuery=(lookUpField:Field,key:any)=>{
|
|
||||||
if(typeof key==='number'){
|
|
||||||
return `${lookUpField.code} = ${key}`
|
|
||||||
}
|
|
||||||
if(typeof key==='string'){
|
|
||||||
return `${lookUpField.code} = "${key}"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新対象のレコードを取得する
|
|
||||||
*/
|
|
||||||
getUpdateRecords = async (lookUpField:Field,key:any):Promise< Record[]>=>{
|
|
||||||
const client=new KintoneRestAPIClient();
|
|
||||||
const resp = await client.record.getAllRecords({
|
|
||||||
app:this.props.lookupField.app.id,
|
|
||||||
fields:["$id"],
|
|
||||||
condition:this.makeQuery(lookUpField,key)
|
|
||||||
});
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ルックアップ更新用レコードに変換する
|
|
||||||
* @param targetRecords 更新対象レコード
|
|
||||||
* @param lookUpField ルックアップフィールド
|
|
||||||
* @param key ルックアップフィールドの値
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
convertForLookup = (targetRecords:Record[],lookUpField:Field,key:any):Array<any>=>{
|
|
||||||
return targetRecords.map((r) => ({
|
|
||||||
id: Number(r["$id"].value),
|
|
||||||
record: { [lookUpField.code]: { value: key } },
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ルックアップ先を更新する
|
|
||||||
* @param updateRecords
|
|
||||||
*/
|
|
||||||
updateLookupTarget = async (updateRecords:Array<any>)=>{
|
|
||||||
if (updateRecords && updateRecords.length > 0) {
|
|
||||||
const client=new KintoneRestAPIClient();
|
|
||||||
client.record.updateAllRecords({
|
|
||||||
app:this.props.lookupField.app.id,
|
|
||||||
records:updateRecords
|
|
||||||
});
|
|
||||||
// await kintone.api(kintone.api.url("/k/v1/records.json", true), "PUT", {
|
|
||||||
// app: this.props.lookupField.app.id,
|
|
||||||
// records: updateRecords
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新中のダイアログ表示
|
|
||||||
* @param app
|
|
||||||
*/
|
|
||||||
showSpinnerModel = (app:App) => {
|
|
||||||
let dialog = $("#alcLookupModal");
|
|
||||||
if(dialog.length===0){
|
|
||||||
const modalHTML = `
|
|
||||||
<div class="modal" id="alcLookupModal" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog-centered">
|
|
||||||
<div class="modal-dialog modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5" id="alcLookupModalLabel">ルックアップ同期処理</h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="row" id="app${app.id}">
|
|
||||||
<div class="spinner-border text-secondary col-1 " role="alert"></div>
|
|
||||||
<div class="col">${app.name}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
dialog = $(modalHTML).appendTo("body");
|
|
||||||
dialog.get()[0].addEventListener('hidden.bs.modal',(ev)=>{
|
|
||||||
Modal.getOrCreateInstance(dialog.get()[0]).dispose();
|
|
||||||
$("#alcLookupModal").remove();
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
const dialogBody=$("#alcLookupModal .modal-body");
|
|
||||||
const htmlrow=`
|
|
||||||
<div class="row" id="app${app.id}">
|
|
||||||
<div class="spinner-border text-secondary col-1 " role="alert">
|
|
||||||
</div>
|
|
||||||
<div class="col">${app.name}</div>
|
|
||||||
<div>`;
|
|
||||||
dialogBody.append(htmlrow);
|
|
||||||
}
|
|
||||||
Modal.getOrCreateInstance(dialog.get()[0]).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新結果を表示する
|
|
||||||
* @param app 更新先アプリ情報
|
|
||||||
* @param count 更新件数
|
|
||||||
*/
|
|
||||||
showResult=(app:App,count:number)=>{
|
|
||||||
const dialogBody=$(`#alcLookupModal .modal-body #app${app.id}`);
|
|
||||||
const html=` <div class="col-1 text-success">✔</div>
|
|
||||||
<div class="col">${app.name}</div>
|
|
||||||
<div class="col">更新件数:${count}件</div>`;
|
|
||||||
dialogBody.html(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name] = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new AutoLookUpAction();
|
|
||||||
24
plugin/kintone-addins/src/actions/auto-numbering.css
Normal file
24
plugin/kintone-addins/src/actions/auto-numbering.css
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
import { actionAddins } from ".";
|
import { actionAddins } from ".";
|
||||||
import { IField, IAction,IActionResult, IActionNode, IActionProperty, IContext, IVarName } from "../types/ActionTypes";
|
import { IField, IAction,IActionResult, IActionNode, IActionProperty, IContext } from "../types/ActionTypes";
|
||||||
import { Formatter } from "../util/format";
|
import { Formatter } from "../util/format";
|
||||||
|
import "./auto-numbering.css";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window { $format: any; }
|
interface Window { $format: any; }
|
||||||
@@ -13,7 +14,7 @@ interface IAutoNumberingProps{
|
|||||||
format:string;
|
format:string;
|
||||||
prefix:string;
|
prefix:string;
|
||||||
suffix:string;
|
suffix:string;
|
||||||
verName:IVarName;
|
verName:string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AutoNumbering implements IAction{
|
export class AutoNumbering implements IAction{
|
||||||
@@ -29,7 +30,7 @@ export class AutoNumbering implements IAction{
|
|||||||
format:'',
|
format:'',
|
||||||
prefix:'',
|
prefix:'',
|
||||||
suffix:'',
|
suffix:'',
|
||||||
verName:{name:''}
|
verName:''
|
||||||
}
|
}
|
||||||
globalThis.window.$format=this.format;
|
globalThis.window.$format=this.format;
|
||||||
this.register();
|
this.register();
|
||||||
@@ -56,8 +57,8 @@ export class AutoNumbering implements IAction{
|
|||||||
const docNum = await this.createNumber(this.props);
|
const docNum = await this.createNumber(this.props);
|
||||||
record[this.props.field.code].value=docNum;
|
record[this.props.field.code].value=docNum;
|
||||||
//変数設定
|
//変数設定
|
||||||
if(this.props.verName && this.props.verName.name!==''){
|
if(this.props.verName){
|
||||||
context.variables[this.props.verName.name]=docNum;
|
context.variables[this.props.verName]=docNum;
|
||||||
}
|
}
|
||||||
result= {
|
result= {
|
||||||
canNext:true,
|
canNext:true,
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
.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;
|
|
||||||
}
|
|
||||||
@@ -10,18 +10,12 @@ import "./button-add.css";
|
|||||||
interface IButtonAddProps {
|
interface IButtonAddProps {
|
||||||
//ボタン表示名
|
//ボタン表示名
|
||||||
buttonName: string;
|
buttonName: string;
|
||||||
space?:ISpace;
|
|
||||||
//配置位置
|
//配置位置
|
||||||
position: string;
|
position: string;
|
||||||
//イベント名
|
//イベント名
|
||||||
eventName:string
|
eventName:string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISpace{
|
|
||||||
type:string,
|
|
||||||
elementId:string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ButtonAddAction implements IAction {
|
export class ButtonAddAction implements IAction {
|
||||||
name: string;
|
name: string;
|
||||||
actionProps: IActionProperty[];
|
actionProps: IActionProperty[];
|
||||||
@@ -55,19 +49,20 @@ export class ButtonAddAction implements IAction {
|
|||||||
}
|
}
|
||||||
this.props = actionNode.ActionValue as IButtonAddProps;
|
this.props = actionNode.ActionValue as IButtonAddProps;
|
||||||
//ボタンを配置する
|
//ボタンを配置する
|
||||||
let buttonSpace;
|
const menuSpace = kintone.app.record.getHeaderMenuSpaceElement();
|
||||||
if(this.props.space && this.props.space.elementId){
|
if(!menuSpace) return result;
|
||||||
buttonSpace = kintone.app.record.getSpaceElement(this.props.space.elementId);
|
if($("style#alc-button-add").length===0){
|
||||||
}else{
|
const css=`
|
||||||
buttonSpace = kintone.app.record.getHeaderMenuSpaceElement();
|
`;
|
||||||
|
const style = $("<style id='alc-button-add'>/<style>");
|
||||||
|
style.text(css);
|
||||||
|
$("head").append(style);
|
||||||
}
|
}
|
||||||
if(!buttonSpace) return result;
|
|
||||||
|
|
||||||
const button =$(`<button id='${this.props.eventName}' class='alc-button-normal' >${this.props.buttonName}</button>`);
|
const button =$(`<button id='${this.props.eventName}' class='alc-button-normal' >${this.props.buttonName}</button>`);
|
||||||
if(this.props.position==="一番左に追加する"){
|
if(this.props.position==="一番左に追加する"){
|
||||||
$(buttonSpace).prepend(button);
|
$(menuSpace).prepend(button);
|
||||||
}else{
|
}else{
|
||||||
$(buttonSpace).append(button);
|
$(menuSpace).append(button);
|
||||||
}
|
}
|
||||||
const clickEventName = `${event.type}.customButtonClick.${this.props.eventName}`;
|
const clickEventName = `${event.type}.customButtonClick.${this.props.eventName}`;
|
||||||
button.on("click",()=>{
|
button.on("click",()=>{
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
import { actionAddins } from ".";
|
import { actionAddins } from ".";
|
||||||
import { IAction,IActionResult, IActionNode, IActionProperty, IContext, IVarName } from "../types/ActionTypes";
|
import { IAction,IActionResult, IActionNode, IActionProperty, IContext } from "../types/ActionTypes";
|
||||||
import { ConditionTree } from '../types/Conditions';
|
import { ConditionTree } from '../types/Conditions';
|
||||||
/**
|
/**
|
||||||
* アクションの属性定義
|
* アクションの属性定義
|
||||||
*/
|
*/
|
||||||
interface ICondition{
|
interface ICondition{
|
||||||
condition:string;
|
condition:string;
|
||||||
verName:IVarName;
|
verName:string;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 条件分岐アクション
|
* 条件分岐アクション
|
||||||
@@ -21,7 +21,7 @@ export class ConditionAction implements IAction{
|
|||||||
this.actionProps=[];
|
this.actionProps=[];
|
||||||
this.props={
|
this.props={
|
||||||
condition:'',
|
condition:'',
|
||||||
verName:{name:''}
|
verName:''
|
||||||
}
|
}
|
||||||
//アクションを登録する
|
//アクションを登録する
|
||||||
this.register();
|
this.register();
|
||||||
@@ -58,8 +58,8 @@ export class ConditionAction implements IAction{
|
|||||||
result:'いいえ'
|
result:'いいえ'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.props.verName && this.props.verName.name!==''){
|
if(this.props.verName){
|
||||||
context.variables[this.props.verName.name]=result.result;
|
context.variables[this.props.verName]=result.result;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}catch(error){
|
}catch(error){
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction,IActionResult, IActionNode, IActionProperty, IField} from "../types/ActionTypes";
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface IStrCountCheckProps{
|
|
||||||
field:IField;//チェックするフィールドの対象
|
|
||||||
message:string;//エラーメッセージ
|
|
||||||
maxLength:number;//
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 正規表現チェックアクション
|
|
||||||
*/
|
|
||||||
export class StrCountCheckAciton implements IAction{
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props:IStrCountCheckProps;
|
|
||||||
constructor(){
|
|
||||||
this.name="文字数チェック";
|
|
||||||
this.actionProps=[];
|
|
||||||
this.props={
|
|
||||||
field:{code:''},
|
|
||||||
message:'',
|
|
||||||
maxLength:0
|
|
||||||
}
|
|
||||||
//アクションを登録する
|
|
||||||
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 (!('field' in actionNode.ActionValue) && !('message' in actionNode.ActionValue) && !('strExpression'in actionNode.ActionValue)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props = actionNode.ActionValue as IStrCountCheckProps;
|
|
||||||
//条件式の計算結果を取得
|
|
||||||
const record = event.record;
|
|
||||||
const value = record[this.props.field.code].value;
|
|
||||||
const maxLength = this.props.maxLength;
|
|
||||||
if(value === undefined || value === '' ){
|
|
||||||
return result;
|
|
||||||
}else if(maxLength < value.length){
|
|
||||||
record[this.props.field.code].error = this.props.message;
|
|
||||||
}else{
|
|
||||||
result= {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}catch(error){
|
|
||||||
event.error=error;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext=false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name]=this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
new StrCountCheckAciton();
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import {
|
|
||||||
IAction,
|
|
||||||
IActionResult,
|
|
||||||
IActionNode,
|
|
||||||
IActionProperty,
|
|
||||||
IContext,
|
|
||||||
} from "../types/ActionTypes";
|
|
||||||
import { actionAddins } from ".";
|
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
displayName: string;
|
|
||||||
field: Field;
|
|
||||||
verName: VerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface VerName {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Field {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
code: string;
|
|
||||||
label: string;
|
|
||||||
noLabel: boolean;
|
|
||||||
required: boolean;
|
|
||||||
minLength: string;
|
|
||||||
maxLength: string;
|
|
||||||
expression: string;
|
|
||||||
hideExpression: boolean;
|
|
||||||
unique: boolean;
|
|
||||||
defaultValue: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CurrentFieldGetAction implements IAction {
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
currentFieldGetProps: Props;
|
|
||||||
constructor() {
|
|
||||||
this.name = "フィールドの値を取得する";
|
|
||||||
this.actionProps = [];
|
|
||||||
this.currentFieldGetProps = {} as Props;
|
|
||||||
this.register();
|
|
||||||
}
|
|
||||||
|
|
||||||
async process(
|
|
||||||
prop: IActionNode,
|
|
||||||
event: any,
|
|
||||||
context: IContext
|
|
||||||
): Promise<IActionResult> {
|
|
||||||
this.currentFieldGetProps = prop.ActionValue as Props;
|
|
||||||
this.actionProps = prop.actionProps;
|
|
||||||
|
|
||||||
let result = {
|
|
||||||
canNext: true,
|
|
||||||
result: "",
|
|
||||||
} as IActionResult;
|
|
||||||
|
|
||||||
try {
|
|
||||||
context.variables[this.currentFieldGetProps.verName.name] = context.record[this.currentFieldGetProps.field.code].value;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("CurrentFieldGetAction error", error);
|
|
||||||
result.canNext = false;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name] = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new CurrentFieldGetAction();
|
|
||||||
182
plugin/kintone-addins/src/actions/data-mapping.ts
Normal file
182
plugin/kintone-addins/src/actions/data-mapping.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import {
|
||||||
|
IAction,
|
||||||
|
IActionResult,
|
||||||
|
IActionNode,
|
||||||
|
IActionProperty,
|
||||||
|
IContext,
|
||||||
|
} from "../types/ActionTypes";
|
||||||
|
import { actionAddins } from ".";
|
||||||
|
|
||||||
|
export type IApp = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
export type IField = {
|
||||||
|
name: string;
|
||||||
|
code: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IAppFields = {
|
||||||
|
app?: IApp;
|
||||||
|
fields: IField[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type ValueType = {
|
||||||
|
id: string;
|
||||||
|
from: {
|
||||||
|
objectType: "variable" | "field";
|
||||||
|
name: { name: string };
|
||||||
|
code: string;
|
||||||
|
};
|
||||||
|
to: IAppFields & {
|
||||||
|
isDialogVisible: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = { app: IApp; field: ValueType[] };
|
||||||
|
|
||||||
|
export class DataMappingAction implements IAction {
|
||||||
|
name: string;
|
||||||
|
actionProps: IActionProperty[];
|
||||||
|
dataMappingProps: Props;
|
||||||
|
constructor() {
|
||||||
|
this.name = "DataMapping";
|
||||||
|
this.actionProps = [];
|
||||||
|
this.dataMappingProps = {} as Props;
|
||||||
|
this.register();
|
||||||
|
}
|
||||||
|
|
||||||
|
async process(
|
||||||
|
prop: IActionNode,
|
||||||
|
event: any,
|
||||||
|
context: IContext
|
||||||
|
): Promise<IActionResult> {
|
||||||
|
this.initActionProps(prop);
|
||||||
|
this.initTypedActionProps();
|
||||||
|
let result = {
|
||||||
|
canNext: true,
|
||||||
|
result: "",
|
||||||
|
} as IActionResult;
|
||||||
|
try {
|
||||||
|
for (const item of this.dataMappingProps.field) {
|
||||||
|
if (item.from.objectType === "variable") {
|
||||||
|
if (
|
||||||
|
item.from.name.name &&
|
||||||
|
item.to.app &&
|
||||||
|
item.to.fields &&
|
||||||
|
item.to.fields.length > 0
|
||||||
|
) {
|
||||||
|
const value = getValueByPath(
|
||||||
|
context.variables,
|
||||||
|
item.from.name.name
|
||||||
|
);
|
||||||
|
if (value) {
|
||||||
|
await kintone.api(
|
||||||
|
kintone.api.url("/k/v1/record.json", true),
|
||||||
|
"POST",
|
||||||
|
{
|
||||||
|
app: item.to.app.id,
|
||||||
|
record: {
|
||||||
|
[item.to.fields[0].code]: {
|
||||||
|
value: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (item.from.objectType === "field") {
|
||||||
|
if (
|
||||||
|
item.from.code &&
|
||||||
|
item.to.app &&
|
||||||
|
item.to.fields &&
|
||||||
|
item.to.fields.length > 0
|
||||||
|
) {
|
||||||
|
const value = await selectData(
|
||||||
|
item.to.app.id,
|
||||||
|
item.to.fields[0].code
|
||||||
|
);
|
||||||
|
if (value && value.type === context.record[item.from.code].type) {
|
||||||
|
await kintone.api(
|
||||||
|
kintone.api.url("/k/v1/records.json", true),
|
||||||
|
"POST",
|
||||||
|
{
|
||||||
|
app: item.to.app.id,
|
||||||
|
records: value.value.map((v) => ({
|
||||||
|
[item.to.fields[0].code]: {
|
||||||
|
value: v,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("DataMappingAction error", error);
|
||||||
|
result.canNext = false;
|
||||||
|
}
|
||||||
|
console.log("dataMappingProps", this.dataMappingProps);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private initActionProps(nodes: IActionNode) {
|
||||||
|
this.actionProps = nodes.actionProps;
|
||||||
|
}
|
||||||
|
private initTypedActionProps() {
|
||||||
|
for (const action of this.actionProps) {
|
||||||
|
if (action.component === "DataMapping") {
|
||||||
|
this.dataMappingProps.field = action.props.modelValue as ValueType[];
|
||||||
|
} else if (action.component === "AppSelect") {
|
||||||
|
this.dataMappingProps.app = action.props.modelValue.app as IApp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register(): void {
|
||||||
|
actionAddins[this.name] = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new DataMappingAction();
|
||||||
|
|
||||||
|
const getValueByPath = (obj: any, path: string) => {
|
||||||
|
return path.split(".").reduce((o, k) => (o || {})[k], obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
type Resp = { records: RespRecordType[] };
|
||||||
|
|
||||||
|
type RespRecordType = {
|
||||||
|
[key: string]: {
|
||||||
|
type: string;
|
||||||
|
value: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
type: string;
|
||||||
|
value: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectData = async (appid: string, field: string): Promise<Result> => {
|
||||||
|
return kintone
|
||||||
|
.api(kintone.api.url("/k/v1/records", true), "GET", {
|
||||||
|
app: appid ?? kintone.app.getId(),
|
||||||
|
fields: [field],
|
||||||
|
})
|
||||||
|
.then((resp: Resp) => {
|
||||||
|
const result: Result = { type: "", value: [] };
|
||||||
|
resp.records.forEach((element) => {
|
||||||
|
for (const [key, value] of Object.entries(element)) {
|
||||||
|
if (result.type === "") {
|
||||||
|
result.type = value.type;
|
||||||
|
}
|
||||||
|
result.value.push(value.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -6,166 +6,130 @@ import {
|
|||||||
IContext,
|
IContext,
|
||||||
} from "../types/ActionTypes";
|
} from "../types/ActionTypes";
|
||||||
import { actionAddins } from ".";
|
import { actionAddins } from ".";
|
||||||
import {KintoneRestAPIClient} from '@kintone/rest-api-client';
|
|
||||||
import { Aggregator,Operator} from '../util/aggregates';
|
|
||||||
import { FieldForm } from "../types/FieldLayout";
|
|
||||||
|
|
||||||
interface IDataProcessingProps {
|
|
||||||
displayName: string;
|
|
||||||
sources: Sources;
|
|
||||||
condition: string;
|
|
||||||
verName?: VerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Condition {
|
type DataProcessingProps = {
|
||||||
queryString: string;
|
app: {
|
||||||
}
|
id: string;
|
||||||
|
name: string;
|
||||||
interface VerName {
|
};
|
||||||
name: string;
|
conditionsQuery: string;
|
||||||
actionName: string;
|
propcessing: {
|
||||||
displayName: string;
|
varRootName: string;
|
||||||
vars: Var[];
|
fields: Field[];
|
||||||
}
|
|
||||||
|
|
||||||
interface Var {
|
|
||||||
id: string;
|
|
||||||
field: FieldForm;
|
|
||||||
logicalOperator: CalcOperator;
|
|
||||||
vName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CalcOperator {
|
|
||||||
operator: Operator;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Sources {
|
|
||||||
app: App;
|
|
||||||
fields: FieldForm[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface App {
|
|
||||||
id: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result = {
|
|
||||||
[key: string]: {
|
|
||||||
type: string;
|
|
||||||
value: any[];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type AnyObject = {
|
type Field = {
|
||||||
[key: string]: any;
|
name: string;
|
||||||
|
code: string;
|
||||||
|
type: string;
|
||||||
|
varName: string;
|
||||||
|
operator: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class DataProcessingAction implements IAction {
|
export class DataProcessingAction implements IAction {
|
||||||
name: string;
|
name: string;
|
||||||
actionProps: IActionProperty[];
|
actionProps: IActionProperty[];
|
||||||
props: IDataProcessingProps ;
|
dataProcessingProps: DataProcessingProps | null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.name = "データ処理";
|
this.name = "データ処理";
|
||||||
this.actionProps = [];
|
this.actionProps = [];
|
||||||
this.props = {
|
this.dataProcessingProps = null;
|
||||||
displayName:'',
|
|
||||||
condition:'',
|
|
||||||
sources:{
|
|
||||||
app:{
|
|
||||||
id:""
|
|
||||||
},
|
|
||||||
fields:[]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
this.register();
|
this.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
async process(
|
async process(
|
||||||
nodes: IActionNode,
|
nodes: IActionNode,event: any,context: IContext
|
||||||
event: any,
|
|
||||||
context: IContext
|
|
||||||
): Promise<IActionResult> {
|
): Promise<IActionResult> {
|
||||||
this.actionProps = nodes.actionProps;
|
this.initActionProps(nodes);
|
||||||
|
this.initTypedActionProps();
|
||||||
this.props = nodes.ActionValue as IDataProcessingProps;
|
|
||||||
const condition = JSON.parse(this.props.condition) as Condition;
|
|
||||||
let result = {
|
let result = {
|
||||||
canNext: true,
|
canNext: true,
|
||||||
result: "",
|
result: "",
|
||||||
} as IActionResult;
|
} as IActionResult;
|
||||||
try {
|
try {
|
||||||
if (!this.props) {
|
if (!this.dataProcessingProps) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = this.getQuery(condition.queryString,context.variables);
|
const data = await selectData(this.dataProcessingProps.conditionsQuery);
|
||||||
const data = await this.selectData(query);
|
|
||||||
|
|
||||||
console.log("data ", data);
|
console.log("data ", data);
|
||||||
|
|
||||||
if(this.props.verName){
|
context.variables[this.dataProcessingProps.propcessing.varRootName] =
|
||||||
const varValues= this.props.verName.vars.reduce((acc, f) => {
|
this.dataProcessingProps.propcessing.fields.reduce((acc, f) => {
|
||||||
const datas=data[f.field.code].value;
|
const v = calc(f, data);
|
||||||
const agg = new Aggregator(datas,f.field);
|
if (v) {
|
||||||
const result = agg.calculate(f.logicalOperator.operator)
|
acc[f.varName] = calc(f, data);
|
||||||
acc[f.vName]=result;
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as AnyObject);
|
}, {} as Var);
|
||||||
context.variables[this.props.verName.name]=varValues;
|
|
||||||
console.log("context ", context);
|
console.log("context ", context);
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("データ集計中例外が発生しました。", error);
|
console.error(error);
|
||||||
if(error instanceof Error){
|
event.error=error;
|
||||||
event.error = `データ集計中例外が発生しました。 ${error.message}`;
|
|
||||||
}else{
|
|
||||||
event.error = "データ集計中例外が発生しました。";
|
|
||||||
}
|
|
||||||
result.canNext = false;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
register(): void {
|
||||||
*
|
actionAddins[this.name] = this;
|
||||||
* @param str
|
}
|
||||||
* @param vars
|
|
||||||
* @returns
|
private initActionProps(nodes: IActionNode) {
|
||||||
*/
|
this.actionProps = nodes.actionProps;
|
||||||
getQuery = (str: string, vars: any) => {
|
}
|
||||||
console.log(str);
|
|
||||||
const regex = /var\((.*?)\)/g;
|
private initTypedActionProps() {
|
||||||
let match;
|
this.dataProcessingProps = {
|
||||||
while ((match = regex.exec(str)) !== null) {
|
app: {
|
||||||
const varName = match[1];
|
id: "",
|
||||||
if (varName in vars) {
|
name: "",
|
||||||
str = str.replace(match[0], vars[varName]);
|
},
|
||||||
} else {
|
conditionsQuery: "",
|
||||||
throw new Error(`変数${varName}が見つかりません`);
|
propcessing: {
|
||||||
|
varRootName: "",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
for (const action of this.actionProps) {
|
||||||
|
if (action.component === "AppFieldSelect") {
|
||||||
|
this.dataProcessingProps.app.id = action.props.modelValue.app.id;
|
||||||
|
this.dataProcessingProps.app.name = action.props.modelValue.app.name;
|
||||||
|
} else if (action.component === "DataProcessing") {
|
||||||
|
this.dataProcessingProps.propcessing.varRootName =
|
||||||
|
action.props.modelValue.name;
|
||||||
|
for (const f of action.props.modelValue.vars) {
|
||||||
|
this.dataProcessingProps.propcessing.fields.push({
|
||||||
|
name: f.field.name,
|
||||||
|
code: f.field.code,
|
||||||
|
type: f.field.type,
|
||||||
|
varName: f.vName,
|
||||||
|
operator: f.logicalOperator.operator,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (action.component === "ConditionInput") {
|
||||||
|
this.dataProcessingProps.conditionsQuery = JSON.parse(
|
||||||
|
action.props.modelValue
|
||||||
|
).queryString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(str);
|
}
|
||||||
return str;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
new DataProcessingAction();
|
||||||
* データを取得する
|
|
||||||
* @param query
|
const selectData = async (query?: string) => {
|
||||||
* @returns
|
return kintone
|
||||||
*/
|
.api(kintone.api.url("/k/v1/records", true), "GET", {
|
||||||
selectData = async ( query?: string) => {
|
app: kintone.app.getId(),
|
||||||
const api = new KintoneRestAPIClient();
|
query: query,
|
||||||
const fields = this.props.sources.fields.map((field)=>field.code);
|
})
|
||||||
const resp = await api.record.getAllRecords({
|
.then((resp: Resp) => {
|
||||||
app: this.props.sources.app.id,
|
|
||||||
fields:fields,
|
|
||||||
condition:query
|
|
||||||
});
|
|
||||||
const result: Result = {};
|
const result: Result = {};
|
||||||
resp.forEach((element) => {
|
resp.records.forEach((element) => {
|
||||||
for (const [key, value] of Object.entries(element)) {
|
for (const [key, value] of Object.entries(element)) {
|
||||||
if (!result[key]) {
|
if (!result[key]) {
|
||||||
result[key] = { type: value.type, value: [] };
|
result[key] = { type: value.type, value: [] };
|
||||||
@@ -174,12 +138,143 @@ export class DataProcessingAction implements IAction {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
register(): void {
|
type Resp = { records: RespRecordType[] };
|
||||||
actionAddins[this.name] = this;
|
|
||||||
|
type RespRecordType = {
|
||||||
|
[key: string]: {
|
||||||
|
type: string;
|
||||||
|
value: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Result = {
|
||||||
|
[key: string]: {
|
||||||
|
type: string;
|
||||||
|
value: any[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Var = {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ERROR_TYPE = "ERROR_TYPE";
|
||||||
|
|
||||||
|
const calc = (field: Field, result: Result) => {
|
||||||
|
const type = typeCheck(field.type);
|
||||||
|
if (!type) {
|
||||||
|
return ERROR_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fun =
|
||||||
|
calcFunc[`${type}_${Operator[field.operator as keyof typeof Operator]}`];
|
||||||
|
if (!fun) {
|
||||||
|
return ERROR_TYPE;
|
||||||
|
}
|
||||||
|
const values = result[field.code].value;
|
||||||
|
if (!values) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return fun(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeCheck = (type: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case "RECORD_NUMBER":
|
||||||
|
case "NUMBER":
|
||||||
|
return CalcType.NUMBER;
|
||||||
|
case "SINGLE_LINE_TEXT":
|
||||||
|
case "MULTI_LINE_TEXT":
|
||||||
|
case "RICH_TEXT":
|
||||||
|
return CalcType.STRING;
|
||||||
|
case "DATE":
|
||||||
|
return CalcType.DATE;
|
||||||
|
case "TIME":
|
||||||
|
return CalcType.TIME;
|
||||||
|
case "DATETIME":
|
||||||
|
case "UPDATED_TIME":
|
||||||
|
return CalcType.DATETIME;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Operator {
|
||||||
|
SUM = "SUM",
|
||||||
|
AVG = "AVG",
|
||||||
|
MAX = "MAX",
|
||||||
|
MIN = "MIN",
|
||||||
|
COUNT = "COUNT",
|
||||||
|
FIRST = "FIRST"
|
||||||
}
|
}
|
||||||
|
|
||||||
new DataProcessingAction();
|
enum CalcType {
|
||||||
|
NUMBER = "number",
|
||||||
|
STRING = "string",
|
||||||
|
DATE = "date",
|
||||||
|
TIME = "time",
|
||||||
|
DATETIME = "datetime",
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcFunc: Record<string, (value: string[]) => string | null> = {
|
||||||
|
[`${CalcType.NUMBER}_${Operator.COUNT}`]: (value: string[]) =>
|
||||||
|
value.length.toString(),
|
||||||
|
[`${CalcType.STRING}_${Operator.COUNT}`]: (value: string[]) =>
|
||||||
|
value.length.toString(),
|
||||||
|
[`${CalcType.DATE}_${Operator.COUNT}`]: (value: string[]) =>
|
||||||
|
value.length.toString(),
|
||||||
|
[`${CalcType.TIME}_${Operator.COUNT}`]: (value: string[]) =>
|
||||||
|
value.length.toString(),
|
||||||
|
[`${CalcType.DATETIME}_${Operator.COUNT}`]: (value: string[]) =>
|
||||||
|
value.length.toString(),
|
||||||
|
|
||||||
|
[`${CalcType.NUMBER}_${Operator.SUM}`]: (value: string[]) =>
|
||||||
|
value.reduce((acc, v) => acc + Number(v), 0).toString(),
|
||||||
|
[`${CalcType.NUMBER}_${Operator.AVG}`]: (value: string[]) =>
|
||||||
|
(value.reduce((acc, v) => acc + Number(v), 0) / value.length).toString(),
|
||||||
|
[`${CalcType.NUMBER}_${Operator.MAX}`]: (value: string[]) =>
|
||||||
|
Math.max(...value.map(Number)).toString(),
|
||||||
|
[`${CalcType.NUMBER}_${Operator.MIN}`]: (value: string[]) =>
|
||||||
|
Math.min(...value.map(Number)).toString(),
|
||||||
|
|
||||||
|
[`${CalcType.STRING}_${Operator.SUM}`]: (value: string[]) => value.join(" "),
|
||||||
|
|
||||||
|
[`${CalcType.DATE}_${Operator.MAX}`]: (value: string[]) =>
|
||||||
|
value.reduce((maxDate, currentDate) =>
|
||||||
|
maxDate > currentDate ? maxDate : currentDate
|
||||||
|
),
|
||||||
|
|
||||||
|
[`${CalcType.DATE}_${Operator.MIN}`]: (value: string[]) =>
|
||||||
|
value.reduce((minDate, currentDate) =>
|
||||||
|
minDate < currentDate ? minDate : currentDate
|
||||||
|
),
|
||||||
|
|
||||||
|
[`${CalcType.TIME}_${Operator.MAX}`]: (value: string[]) =>
|
||||||
|
value.reduce((maxTime, currentTime) =>
|
||||||
|
maxTime > currentTime ? maxTime : currentTime
|
||||||
|
),
|
||||||
|
[`${CalcType.TIME}_${Operator.MIN}`]: (value: string[]) =>
|
||||||
|
value.reduce((minTime, currentTime) =>
|
||||||
|
minTime < currentTime ? minTime : currentTime
|
||||||
|
),
|
||||||
|
|
||||||
|
[`${CalcType.DATETIME}_${Operator.MAX}`]: (value: string[]) =>
|
||||||
|
value.reduce((maxDateTime, currentDateTime) =>
|
||||||
|
new Date(maxDateTime) > new Date(currentDateTime)
|
||||||
|
? maxDateTime
|
||||||
|
: currentDateTime
|
||||||
|
),
|
||||||
|
|
||||||
|
[`${CalcType.DATETIME}_${Operator.MIN}`]: (value: string[]) =>
|
||||||
|
value.reduce((minDateTime, currentDateTime) =>
|
||||||
|
new Date(minDateTime) < new Date(currentDateTime)
|
||||||
|
? minDateTime
|
||||||
|
: currentDateTime
|
||||||
|
),
|
||||||
|
[`${CalcType.STRING}_${Operator.FIRST}`]:(value: string[])=>{
|
||||||
|
return value[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,334 +0,0 @@
|
|||||||
import {
|
|
||||||
IAction,
|
|
||||||
IActionResult,
|
|
||||||
IActionNode,
|
|
||||||
IActionProperty,
|
|
||||||
IContext,
|
|
||||||
} from "../types/ActionTypes";
|
|
||||||
import { actionAddins } from ".";
|
|
||||||
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
|
|
||||||
import { Lookup } from "@kintone/rest-api-client/lib/src/KintoneFields/types/property";
|
|
||||||
import { FieldForm, FieldType } from "../types/FieldLayout";
|
|
||||||
interface Props {
|
|
||||||
displayName: string;
|
|
||||||
sources: Sources;
|
|
||||||
dataMapping: DataMapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DataMapping {
|
|
||||||
data: Mapping[];
|
|
||||||
createWithNull: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Mapping {
|
|
||||||
id: string;
|
|
||||||
from: From;
|
|
||||||
to: To;
|
|
||||||
isKey: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface To {
|
|
||||||
app: App;
|
|
||||||
fields:FieldForm[];
|
|
||||||
isDialogVisible: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface From {
|
|
||||||
sharedText: string;
|
|
||||||
id: string;
|
|
||||||
objectType: 'variable'|'field'|'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IVar extends From{
|
|
||||||
name:{
|
|
||||||
name:string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFromField extends From,FieldForm{
|
|
||||||
|
|
||||||
}
|
|
||||||
interface Sources {
|
|
||||||
app: App;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface App {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
createdate: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DataUpdateAction implements IAction {
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
dataMappingProps: Props;
|
|
||||||
constructor() {
|
|
||||||
this.name = "データ更新";
|
|
||||||
this.actionProps = [];
|
|
||||||
this.dataMappingProps = {} as Props;
|
|
||||||
this.register();
|
|
||||||
}
|
|
||||||
|
|
||||||
async process(
|
|
||||||
prop: IActionNode,
|
|
||||||
event: any,
|
|
||||||
context: IContext
|
|
||||||
): Promise<IActionResult> {
|
|
||||||
this.actionProps = prop.actionProps;
|
|
||||||
this.dataMappingProps = prop.ActionValue as Props;
|
|
||||||
console.log(context);
|
|
||||||
let result = {
|
|
||||||
canNext: true,
|
|
||||||
result: "",
|
|
||||||
} as IActionResult;
|
|
||||||
try {
|
|
||||||
const lookupFixedFieldCodes = await getLookupFixedFieldCodes(
|
|
||||||
this.dataMappingProps.sources.app.id
|
|
||||||
);
|
|
||||||
|
|
||||||
// createWithNull が有効な場合は、4 番目のパラメーターを true にして doUpdate 関数ブランチを実行します。
|
|
||||||
if (this.dataMappingProps.dataMapping.createWithNull) {
|
|
||||||
await doUpdate(
|
|
||||||
this.dataMappingProps.dataMapping.data,
|
|
||||||
this.dataMappingProps.sources.app.id,
|
|
||||||
context,
|
|
||||||
true, // キーがない場合、またはキーでターゲットが見つからない場合に、マッピング条件によって新しいレコードを作成するかどうかを決定するために使用されます。
|
|
||||||
lookupFixedFieldCodes
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
// キーがないと更新対象を取得できないため、この時点でのみ更新が行われます。 doUpdate 関数の 4 番目のパラメーターは false です。
|
|
||||||
this.dataMappingProps.dataMapping.data
|
|
||||||
.map((m) => m.isKey)
|
|
||||||
.find((isKey) => isKey === true)
|
|
||||||
) {
|
|
||||||
await doUpdate(
|
|
||||||
this.dataMappingProps.dataMapping.data,
|
|
||||||
this.dataMappingProps.sources.app.id,
|
|
||||||
context,
|
|
||||||
false,
|
|
||||||
lookupFixedFieldCodes
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await doCreate(
|
|
||||||
this.dataMappingProps.dataMapping.data,
|
|
||||||
this.dataMappingProps.sources.app.id,
|
|
||||||
context,
|
|
||||||
lookupFixedFieldCodes
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("DataMappingAction error", error);
|
|
||||||
result.canNext = false;
|
|
||||||
}
|
|
||||||
console.log("dataMappingProps", this.dataMappingProps);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name] = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new DataUpdateAction();
|
|
||||||
|
|
||||||
const getContextVarByPath = (obj: any, path: string) => {
|
|
||||||
return path.split(".").reduce((o, k) => (o || {})[k], obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface UpdateRecord {
|
|
||||||
id: string;
|
|
||||||
record: {
|
|
||||||
[key: string]: {
|
|
||||||
value: any;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new KintoneRestAPIClient();
|
|
||||||
|
|
||||||
const getFromValue=(item:Mapping,context:IContext)=>{
|
|
||||||
if (item.from.objectType === "variable") {
|
|
||||||
const rfrom =item.from as IVar;
|
|
||||||
return getContextVarByPath(context.variables,rfrom.name.name);
|
|
||||||
}else if(item.from.objectType === "field"){
|
|
||||||
const field = item.from as IFromField;
|
|
||||||
return context.record[field.code].value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return item.from.sharedText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const doUpdate = async (
|
|
||||||
mappingData: Mapping[],
|
|
||||||
appId: string,
|
|
||||||
context: IContext,
|
|
||||||
needCreate: boolean,
|
|
||||||
lookupFixedFieldCodes: string[]
|
|
||||||
) => {
|
|
||||||
const targetField = await findUpdateField(mappingData, appId, context);
|
|
||||||
console.log(targetField);
|
|
||||||
if (targetField.records.length === 0 && needCreate) {
|
|
||||||
await doCreate(mappingData, appId, context, lookupFixedFieldCodes);
|
|
||||||
} else {
|
|
||||||
// マッピングデータを単純なオブジェクトに処理し、ソース値が変数の場合は変数を置き換えます。
|
|
||||||
const mappingRules = mappingData
|
|
||||||
.filter(
|
|
||||||
(m) =>
|
|
||||||
Object.keys(m.from).length > 0 &&
|
|
||||||
!lookupFixedFieldCodes.includes(m.to.fields[0].code)
|
|
||||||
)
|
|
||||||
.map((m) => {
|
|
||||||
if (m.from.objectType === "variable") {
|
|
||||||
const rfrom =m.from as IVar;
|
|
||||||
return {
|
|
||||||
value: getContextVarByPath(context.variables,rfrom.name.name),
|
|
||||||
code: m.to.fields[0].code,
|
|
||||||
};
|
|
||||||
}else if(m.from.objectType === "field"){
|
|
||||||
const field = m.from as IFromField;
|
|
||||||
return {
|
|
||||||
value: context.record[field.code].value,
|
|
||||||
code: m.to.fields[0].code,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return {
|
|
||||||
value: m.from.sharedText,
|
|
||||||
code: m.to.fields[0].code,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateRecords: UpdateRecord[] = targetField.records.map(
|
|
||||||
(targetRecord) => {
|
|
||||||
const updateRecord: UpdateRecord["record"] = {};
|
|
||||||
|
|
||||||
// マッピング内のルールにヒットしたフィールドのみが更新されます。
|
|
||||||
for (const mapping of mappingRules) {
|
|
||||||
if (targetRecord[mapping.code]) {
|
|
||||||
updateRecord[mapping.code] = {
|
|
||||||
value: mapping.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: targetRecord.$id.value as string,
|
|
||||||
record: updateRecord,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(updateRecords);
|
|
||||||
await client.record.updateRecords({
|
|
||||||
app: appId,
|
|
||||||
records: updateRecords,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeQuery=(field:FieldForm,key:any)=>{
|
|
||||||
if(field.type===FieldType.NUMBER || field.type===FieldType.RECORD_NUMBER){
|
|
||||||
return `${field.code} = ${Number(key)}`
|
|
||||||
}
|
|
||||||
if(typeof key==='string'){
|
|
||||||
return `${field.code} = "${key}"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const findUpdateField = async (
|
|
||||||
mappingData: Mapping[],
|
|
||||||
appId: string,
|
|
||||||
context: IContext
|
|
||||||
) => {
|
|
||||||
const queryStr = mappingData
|
|
||||||
.filter((m) => m.to.app && m.to.fields && m.to.fields.length > 0 && m.isKey)
|
|
||||||
.map((m) => {
|
|
||||||
if (m.from.objectType === "variable") {
|
|
||||||
const vfrom = m.from as IVar;
|
|
||||||
return makeQuery(m.to.fields[0],getContextVarByPath(context.variables , vfrom.name.name));
|
|
||||||
}
|
|
||||||
else if(m.from.objectType === "field"){
|
|
||||||
const field = m.from as IFromField;
|
|
||||||
return makeQuery(m.to.fields[0],context.record[field.code].value);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return makeQuery(m.to.fields[0],m.from.sharedText);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.join("&");
|
|
||||||
// 検索条件が空の場合は全レコードを返すため、検索対象が見つからない場合は検索は行われません。
|
|
||||||
if (queryStr.length === 0) {
|
|
||||||
return {
|
|
||||||
records: [],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return await client.record.getRecords({
|
|
||||||
app: appId,
|
|
||||||
// query: undefined
|
|
||||||
query: queryStr,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const doCreate = async (
|
|
||||||
mappingData: Mapping[],
|
|
||||||
appId: string,
|
|
||||||
context: IContext,
|
|
||||||
lookupFixedFieldCodes: string[]
|
|
||||||
) => {
|
|
||||||
const filterHandler = (item:Mapping)=>{
|
|
||||||
if(!item.to.fields || item.to.fields.length===0){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(item.from.objectType === "variable" && (item.from as IVar).name.name ){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(item.from.objectType === "field" && (item.from as IFromField).code){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(item.from.objectType === "text" && item.from.sharedText!==null){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const record = mappingData
|
|
||||||
.filter(filterHandler)
|
|
||||||
.filter((item) => !lookupFixedFieldCodes.includes(item.to.fields[0].code))
|
|
||||||
.reduce((accumulator, item) => {
|
|
||||||
return {
|
|
||||||
...accumulator,
|
|
||||||
[item.to.fields[0].code]: {
|
|
||||||
value: getFromValue(item,context),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}, {});
|
|
||||||
if (record && Object.keys(record).length > 0) {
|
|
||||||
console.log(record);
|
|
||||||
await client.record.addRecord({
|
|
||||||
app:appId,
|
|
||||||
record:record
|
|
||||||
});
|
|
||||||
// await kintone.api(kintone.api.url("/k/v1/record.json", true), "POST", {
|
|
||||||
// app: appId,
|
|
||||||
// record: record,
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLookupFixedFieldCodes = async (appId: string) => {
|
|
||||||
return await client.app
|
|
||||||
.getFormFields({ app: appId })
|
|
||||||
.then((resp) =>
|
|
||||||
Object.values(resp.properties)
|
|
||||||
.filter((f) => (f as Lookup).lookup !== undefined)
|
|
||||||
.flatMap((f) => (f as Lookup).lookup.fieldMappings.map((m) => m.field))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction, IActionResult, IActionNode, IActionProperty, IField ,IContext, IVarName} from "../types/ActionTypes";
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface IDatetimeGetterProps {
|
|
||||||
/**変数の名前 */
|
|
||||||
verName:IVarName;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 現在日時を取得するアクション
|
|
||||||
*/
|
|
||||||
export class DatetimeGetterAction implements IAction {
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props: IDatetimeGetterProps;
|
|
||||||
constructor() {
|
|
||||||
this.name = "現在日時";
|
|
||||||
this.actionProps = [];
|
|
||||||
this.props = {
|
|
||||||
verName:{name:''}
|
|
||||||
}
|
|
||||||
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 (!('verName' in actionNode.ActionValue) ) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
this.props = actionNode.ActionValue as IDatetimeGetterProps;
|
|
||||||
|
|
||||||
let today = new Date();
|
|
||||||
|
|
||||||
if(this.props.verName && this.props.verName.name!==''){
|
|
||||||
context.variables[this.props.verName.name]=today.toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
event.error = error;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext = false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
new DatetimeGetterAction();
|
|
||||||
@@ -56,8 +56,8 @@ export class FieldShownAction implements IAction{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
result= {
|
result= {
|
||||||
canNext:true,
|
canNext:true,
|
||||||
result:true
|
result:true
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}catch(error){
|
}catch(error){
|
||||||
@@ -69,9 +69,9 @@ export class FieldShownAction implements IAction{
|
|||||||
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param context 条件式を実行する
|
* @param context 条件式を実行する
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getConditionResult(context:any):boolean{
|
getConditionResult(context:any):boolean{
|
||||||
const tree =this.getCondition(this.props.condition);
|
const tree =this.getCondition(this.props.condition);
|
||||||
@@ -84,7 +84,7 @@ export class FieldShownAction implements IAction{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param condition 条件式ツリーを取得する
|
* @param condition 条件式ツリーを取得する
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getCondition(condition:string):ConditionTree|null{
|
getCondition(condition:string):ConditionTree|null{
|
||||||
try{
|
try{
|
||||||
@@ -93,7 +93,7 @@ export class FieldShownAction implements IAction{
|
|||||||
if(tree.getConditions(tree.root).length>0){
|
if(tree.getConditions(tree.root).length>0){
|
||||||
return tree;
|
return tree;
|
||||||
}else{
|
}else{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}catch(error){
|
}catch(error){
|
||||||
return null;
|
return null;
|
||||||
@@ -103,5 +103,6 @@ export class FieldShownAction implements IAction{
|
|||||||
register(): void {
|
register(): void {
|
||||||
actionAddins[this.name]=this;
|
actionAddins[this.name]=this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
new FieldShownAction();
|
new FieldShownAction();
|
||||||
@@ -1,543 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction,IActionResult, IActionNode, IActionProperty, IField, IContext } from "../types/ActionTypes";
|
|
||||||
import { ConditionTree } from '../types/Conditions';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface IInsertValueProps{
|
|
||||||
field:IField;
|
|
||||||
condition:string;
|
|
||||||
value:string;
|
|
||||||
show:string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export class InsertValueAction implements IAction{
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props:IInsertValueProps;
|
|
||||||
constructor(){
|
|
||||||
this.name="値を挿入する";// DBに登録したアクション名
|
|
||||||
this.actionProps=[];
|
|
||||||
//プロパティ属性の初期化
|
|
||||||
this.props={
|
|
||||||
field:{code:''},
|
|
||||||
condition:'',
|
|
||||||
value:'',
|
|
||||||
show:''
|
|
||||||
}
|
|
||||||
//アクションを登録する
|
|
||||||
this.register();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 空白文字を空白文字が非対応のフィールドに挿入しようとしていないか、必須項目フィールドに挿入しようとしていないかチェックする
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {boolean} -入力値が有効な日付形式の場合はtrueを返し、そうでない場合は例外を発生させる
|
|
||||||
*/
|
|
||||||
checkInputBlank(fieldType :string | undefined,inputValue :string,fieldCode :string,fieldRequired :boolean | undefined,event :any): boolean{
|
|
||||||
//正規表現チェック
|
|
||||||
let blankCheck= inputValue.match(/^(\s| )+?$/);//半角スペース・タブ文字・改行・改ページ・全角スペース
|
|
||||||
|
|
||||||
if(blankCheck !== null){
|
|
||||||
//空白文字を空白文字が非対応のフィールドに挿入しようとしている場合、例外を発生させる
|
|
||||||
if(fieldType === "NUMBER" || fieldType === "DATE" || fieldType === "DATETIME" || fieldType === "TIME" || fieldType === "USER_SELECT"
|
|
||||||
|| fieldType === "ORGANIZATION_SELECT" || fieldType === "GROUP_SELECT" || fieldType === "RADIO_BUTTON" || fieldType === "DROP_DOWN" || fieldType === "CHECK_BOX" || fieldType === "MULTI_SELECT"){
|
|
||||||
event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドには、空白文字は挿入できません。"; //レコードにエラーを表示
|
|
||||||
throw new Error("「"+fieldCode+"」"+"フィールドには、空白文字は挿入できません。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
|
|
||||||
//空白文字を必須項目フィールドに挿入しようとしている場合、例外を発生させる
|
|
||||||
}else if(fieldRequired){
|
|
||||||
event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドは必須項目のため、空白文字は挿入できません。"; //レコードにエラーを表示
|
|
||||||
throw new Error("「"+fieldCode+"」"+"フィールドは必須項目のため、空白文字は挿入できません。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 入力値が半角数字かチェックする関数
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {boolean} -入力値が有効な数値の場合はtrueを返し、そうでない場合は例外を発生させる
|
|
||||||
*/
|
|
||||||
checkInputNumber(inputValue :string,fieldCode :string,event :any): boolean{
|
|
||||||
let inputNumberValue = Number(inputValue);//数値型に変換
|
|
||||||
|
|
||||||
//有限数かどうか判定s
|
|
||||||
if(!isFinite(inputNumberValue)){
|
|
||||||
event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。"; //レコードにエラーを表示
|
|
||||||
throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、有効な数値ではありません。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 入力値が有効な日付形式かチェックする関数
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {boolean} -入力値が有効な日付形式の場合はtrueを返し、そうでない場合は例外を発生させる
|
|
||||||
*/
|
|
||||||
checkInputDate(inputValue :string,fieldCode :string,event :any): boolean{
|
|
||||||
//正規表現チェック
|
|
||||||
let twoDigitMonthDay = inputValue.match(/(\d{4})-(\d{2})-(\d{2})$/);//4桁の数字-2桁の数字-2桁の数字
|
|
||||||
let singleDigitMonthDay = inputValue.match(/(\d{4})-(\d{1})-(\d{1})$/);//4桁の数字-1桁の数字-2桁の数字
|
|
||||||
let singleDigitMonth = inputValue.match(/(\d{4})-(\d{1})-(\d{2})$/);//4桁の数字-1桁の数字-2桁の数字
|
|
||||||
let singleDigitDay = inputValue.match(/(\d{4})-(\d{2})-(\d{1})$/);//4桁の数字-2桁の数字-1桁の数字
|
|
||||||
let dateTimeMilliSecond = inputValue.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).(\d{2,3})Z$/);//時刻入りのUTCの日付形式(ミリ秒)
|
|
||||||
let dateTime = inputValue.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/);//時刻入りのUTCの日付形式
|
|
||||||
let date;
|
|
||||||
//date型に変換
|
|
||||||
date = new Date(inputValue);
|
|
||||||
|
|
||||||
//date型変換できたか確認
|
|
||||||
if(date !== undefined && !isNaN(date.getDate())){
|
|
||||||
//正規表現チェック確認
|
|
||||||
if(twoDigitMonthDay === null && singleDigitMonth === null && singleDigitDay === null && singleDigitMonthDay === null && dateTime === null && dateTimeMilliSecond === null){
|
|
||||||
event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。"; //レコードにエラーを表示
|
|
||||||
throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 入力値が有効な時刻形式かチェックする関数
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {boolean} -入力値が有効な日付形式の場合はtrueを返し、そうでない場合は例外を発生させる
|
|
||||||
*/
|
|
||||||
checkInputTime(inputValue :string,fieldCode :string,event :any): boolean{
|
|
||||||
//正規表現チェック
|
|
||||||
let timeFormat =inputValue.match(/^([0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/);
|
|
||||||
|
|
||||||
//正規表現チェック確認
|
|
||||||
if(timeFormat === null){
|
|
||||||
event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な時刻形式です。"; //レコードにエラーを表示
|
|
||||||
throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な時刻形式です。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 入力値のフィールドタイプがDATETIMEであれば、時刻ありの日付形式変換し、DATEであれば時刻なしの日付形式変換する関数
|
|
||||||
* @param {string} inputValue -挿入する値
|
|
||||||
* @param {string} fieldType -フィールドタイプ
|
|
||||||
* @return {string} -入力値が日付形式に変換できた場合は文字列を返し、そうでない場合は例外を発生させる
|
|
||||||
*/
|
|
||||||
changeDateFormat(inputValue :string, fieldType :string,fieldCode :string,event :any): string{
|
|
||||||
let dateTime;
|
|
||||||
let date;
|
|
||||||
|
|
||||||
//挿入する値をdate型に変換
|
|
||||||
date = new Date(inputValue);
|
|
||||||
//date型変換できたか確認
|
|
||||||
if(date !== undefined && !isNaN(date.getDate())){
|
|
||||||
|
|
||||||
//日時フィールドの場合、時刻ありの日付形式変換
|
|
||||||
if(fieldType === "DATETIME"){
|
|
||||||
dateTime =date.toISOString();
|
|
||||||
return dateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
//日付フィールドの場合、時刻なしの日付形式変換
|
|
||||||
let dateArray=inputValue.match(/(\d{4})-(\d{1,2})-(\d{1,2})$/);//4桁の数字-1~2桁の数字-1~2桁の数字
|
|
||||||
if(dateArray !== null){
|
|
||||||
let yearIndex = 1;
|
|
||||||
let monthIndex = 2;
|
|
||||||
let dayIndex = 3;
|
|
||||||
let dateFormatted=`${dateArray[yearIndex]}-${dateArray[monthIndex]}-${dateArray[dayIndex]}`
|
|
||||||
return dateFormatted;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
event.record[fieldCode]['error'] = "「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。"; //レコードにエラーを表示
|
|
||||||
throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした値は、無効な日付形式です。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 入力値がフィールドタイプ(ラジオボタン・ドロップダウン・複数選択・ドロップダウン)の選択肢に存在する値かチェックする関数
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {boolean} -入力値が有効な値の場合はtrueを返し、そうでない場合は例外を発生させる
|
|
||||||
*/
|
|
||||||
checkInputOption(inputValue :string,fieldOptions :string | undefined,fieldCode :string,event :any): boolean {
|
|
||||||
|
|
||||||
//入力値が選択肢に存在する値かチェックし、存在したらtrueを返す
|
|
||||||
if(fieldOptions !== undefined){
|
|
||||||
let options = Object.keys(fieldOptions);
|
|
||||||
for(var optionsIndex in options){
|
|
||||||
if(options[optionsIndex] === inputValue){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event.record[fieldCode]['error']="「"+fieldCode+"」"+"には、存在しない値を挿入しようとしたため、処理を中断しました。";
|
|
||||||
throw new Error("「"+fieldCode+"」"+"には、存在しない値を挿入しようとしたため、処理を中断しました。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 入力値がフィールドタイプ(ユーザー選択)で、ユーザー情報に存在する値かチェックする関数
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {string | boolean} 入力値が登録されているユーザー情報から見つかった場合、trueを返し、見つからなかった場合、falseを返す
|
|
||||||
*/
|
|
||||||
async setInputUser(inputValue :string): Promise<string | boolean>{
|
|
||||||
|
|
||||||
//ユーザー名を格納する変数
|
|
||||||
let usersName;
|
|
||||||
const usersInfoColumnIndex=0;
|
|
||||||
|
|
||||||
try{
|
|
||||||
//APIでユーザー情報を取得する
|
|
||||||
const resp =await kintone.api(kintone.api.url('/v1/users', true), 'GET', {codes:[inputValue ]})
|
|
||||||
|
|
||||||
//入力されたログイン名(メールアドレス)がユーザー情報に登録されている場合、そのユーザー名を取得する
|
|
||||||
if (resp.users[usersInfoColumnIndex].code === inputValue) {
|
|
||||||
usersName=resp.users[usersInfoColumnIndex].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
//ユーザー名が取得できた場合、ログイン名とユーザー名をフィールドにセットする
|
|
||||||
if(usersName === undefined){
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}catch{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return usersName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 入力値がフィールドタイプ(組織選択)で、組織情報に存在する値かチェックする関数
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {string | boolean} 入力値が登録されている組織情報から見つかった場合、trueを返し、見つからなかった場合、falseを返す
|
|
||||||
*/
|
|
||||||
async setInputOrganization(inputValue :string): Promise<string | boolean>{
|
|
||||||
|
|
||||||
//組織名を格納する変数
|
|
||||||
let organizationName;
|
|
||||||
const organizationsInfoColumnIndex=0;
|
|
||||||
|
|
||||||
try{
|
|
||||||
//APIで組織情報を取得する
|
|
||||||
const resp =await kintone.api(kintone.api.url('/v1/organizations.json', true), 'GET', {codes:[inputValue ]})
|
|
||||||
|
|
||||||
//入力された組織コードが組織情報に登録されている場合、その組織名を取得する
|
|
||||||
if (resp.organizations[organizationsInfoColumnIndex].code === inputValue) {
|
|
||||||
organizationName=resp.organizations[organizationsInfoColumnIndex].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
//組織名が取得できた場合、組織コードと組織名をフィールドにセットする
|
|
||||||
if(organizationName === undefined){
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}catch{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return organizationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 入力値がフィールドタイプ(グループ選択)で、グループ情報に存在する値かチェックする関数
|
|
||||||
* @param {string} inputValue - 挿入する値
|
|
||||||
* @return {string | boolean} 入力値が登録されているグループ情報から見つかった場合、trueを返し、見つからなかった場合、falseを返す
|
|
||||||
*/
|
|
||||||
async setInputGroup(inputValue :string): Promise<string | boolean>{
|
|
||||||
|
|
||||||
//グループ名を格納する変数
|
|
||||||
let groupsName;
|
|
||||||
const groupsInfoColumnIndex=0;
|
|
||||||
|
|
||||||
try{
|
|
||||||
//APIでグループ情報を取得する
|
|
||||||
const resp =await kintone.api(kintone.api.url('/v1/groups.json', true), 'GET', {codes:[inputValue ]})
|
|
||||||
|
|
||||||
//入力されたグループコードがグループ情報に登録されている場合、そのグループ名を取得する
|
|
||||||
if (resp.groups[groupsInfoColumnIndex].code === inputValue) {
|
|
||||||
groupsName=resp.groups[groupsInfoColumnIndex].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
//グループ名が取得できた場合、グループコードとグループ名をフィールドにセットする
|
|
||||||
if(groupsName === undefined){
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}catch{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return groupsName;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* アクションの実行を呼び出す
|
|
||||||
* @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) && !('value' in actionNode.ActionValue)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldColumnIndex=1;
|
|
||||||
const valueColumnIndex=3;
|
|
||||||
|
|
||||||
//プロパティで選択されたフィールド
|
|
||||||
const field=this.actionProps[fieldColumnIndex].props.modelValue.type;
|
|
||||||
//プロパティの挿入する値
|
|
||||||
const value=this.actionProps[valueColumnIndex].props.modelValue;
|
|
||||||
|
|
||||||
//条件式の結果を取得
|
|
||||||
const conditionResult = this.getConditionResult(context);
|
|
||||||
if(!conditionResult){
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//プロパティの値を挿入するフィールドが未選択の場合、例外を発生させる
|
|
||||||
if(field === null){
|
|
||||||
throw new Error("「値を挿入する」コンポーネントで、値を挿入するフィールドが指定されていなかったため、処理が中断されました。");
|
|
||||||
}
|
|
||||||
|
|
||||||
//プロパティの値を挿入するフィールドが非対応フィールドの場合、例外を発生させる
|
|
||||||
//添付ファイル・テーブル・カテゴリー・ステータス・作成者・更新者・作業者・リビジョン番号・レコード番号・レコードID・計算・作成日時・更新日時フィールドが選択されている場合、例外を発生させる
|
|
||||||
if(field === "FILE" || field === "SUBTABLE" || field === "CATEGORY" || field === "STATUS"
|
|
||||||
|| field === "STATUS_ASSIGNEE" || field === "CREATOR" || field === "MODIFIER" || field === "__REVISION__"
|
|
||||||
|| field === "RECORD_NUMBER"|| field === "__ID__" || field ==="CALC" || field === "CREATED_TIME" || field === "UPDATED_TIME" ){
|
|
||||||
throw new Error("「値を挿入する」コンポーネントで、選択されたフィールドは、値を挿入するコンポーネントでは非対応のフィールドのため、処理を中断しました。");
|
|
||||||
}
|
|
||||||
|
|
||||||
//プロパティの挿入する値が未入力の場合、例外を発生させる
|
|
||||||
if(value === ""){
|
|
||||||
throw new Error("「値を挿入する」コンポーネントで、フィールドに挿入する値が指定されていなかったため、処理が中断されました。");
|
|
||||||
}
|
|
||||||
|
|
||||||
//既定のプロパティのインターフェースへ変換する
|
|
||||||
this.props = actionNode.ActionValue as IInsertValueProps;
|
|
||||||
|
|
||||||
//挿入する値を取得
|
|
||||||
let fieldValue = this.props.value;
|
|
||||||
//フィールドの種類を取得
|
|
||||||
const fieldType = this.props.field.type;
|
|
||||||
//フィールドが必須項目なのか取得
|
|
||||||
const fieldRequired=this.props.field.required;
|
|
||||||
//挿入するフィールドのコードを取得
|
|
||||||
const fieldCode=this.props.field.code;
|
|
||||||
//挿入する値の形式(手入力か変数)を取得
|
|
||||||
const insertValueType=this.props.show;
|
|
||||||
//ラジオボタン・チェックボックス・複数選択・ドロップダウンの選択肢を取得
|
|
||||||
let fieldOptions =this.props.field.options;
|
|
||||||
//変数の値を格納する
|
|
||||||
let variableValue;
|
|
||||||
|
|
||||||
//変数の場合、値が取得できるかチェック
|
|
||||||
if(insertValueType === "変数" && conditionResult){
|
|
||||||
variableValue = context.variables[fieldValue];
|
|
||||||
|
|
||||||
if(variableValue === undefined){
|
|
||||||
throw new Error("「"+fieldCode+"」"+"フィールドに入れようとした変数は、無効な入力形式です。");
|
|
||||||
}
|
|
||||||
fieldValue = variableValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//入力値チェック後、形式変換、型変換した値を格納する変数
|
|
||||||
let correctFormattedValue;
|
|
||||||
//入力値チェック後、形式変換、型変換した値を格納する配列
|
|
||||||
let correctValues :string[] = [];
|
|
||||||
//入力エラー(空白文字の混入)がないことをチェック
|
|
||||||
let notInputError=this.checkInputBlank(fieldType,fieldValue,fieldCode,fieldRequired,event);
|
|
||||||
|
|
||||||
//条件式の結果がtrue、入力エラー(空白文字の混入)がない場合、挿入する値をフィールドタイプ別にチェックする
|
|
||||||
if(conditionResult && notInputError){
|
|
||||||
|
|
||||||
//文字列型のフィールドに挿入しようとしている値が適切の場合、correctFormattedValueに代入する
|
|
||||||
if(fieldType === "SINGLE_LINE_TEXT" || fieldType === "MULTI_LINE_TEXT" || fieldType === "RICH_TEXT" || fieldType === "LINK" ){
|
|
||||||
correctFormattedValue = fieldValue;
|
|
||||||
|
|
||||||
//数値型のフィールドに挿入しようとしている値が適切の場合、数値型に型変換してcorrectFormattedValueに代入する
|
|
||||||
}else if(fieldType === "NUMBER" ){
|
|
||||||
if(this.checkInputNumber(fieldValue,fieldCode,event)){//入力値チェック
|
|
||||||
correctFormattedValue = Number(fieldValue);//型変換
|
|
||||||
}
|
|
||||||
|
|
||||||
//日付・日時型のフィールドに挿入しようとしている値が適切の場合、指定の日付・日時に形式変換してcorrectFormattedValueに代入する
|
|
||||||
}else if(fieldType === "DATE" || fieldType === "DATETIME" ){
|
|
||||||
if(this.checkInputDate(fieldValue,fieldCode,event)){//入力値チェック
|
|
||||||
let formattedDate = this.changeDateFormat(fieldValue,fieldType,fieldCode,event)
|
|
||||||
if(formattedDate){
|
|
||||||
correctFormattedValue = formattedDate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//時刻フィールドに挿入しようとしている値が適切の場合、correctFormattedValueに代入する
|
|
||||||
}else if(fieldType === "TIME"){
|
|
||||||
if(this.checkInputTime(fieldValue,fieldCode,event)){//入力値チェック
|
|
||||||
correctFormattedValue = fieldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//ラジオボタン・ドロップダウンのフィールドの選択肢と入力値が一致した場合、correctFormattedValueに代入する
|
|
||||||
}else if(fieldType === "RADIO_BUTTON" || fieldType === "DROP_DOWN"){
|
|
||||||
if(this.checkInputOption(fieldValue,fieldOptions,fieldCode,event)){//入力値チェック
|
|
||||||
correctFormattedValue = fieldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//チェックボックス・複数選択のフィールドの選択肢と入力値が一致した場合、correctValuesの配列に代入する
|
|
||||||
}else if(fieldType === "CHECK_BOX" || fieldType === "MULTI_SELECT" ){
|
|
||||||
if(this.checkInputOption(fieldValue,fieldOptions,fieldCode,event)){//入力値チェック
|
|
||||||
correctValues[0] = fieldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//ユーザー情報フィードに挿入しようとした値が適切な場合、correctFormattedValueに代入する
|
|
||||||
}else if(fieldType === "USER_SELECT"){
|
|
||||||
//挿入する値がユーザー情報から見つかれば、ユーザー名を格納
|
|
||||||
let users=await this.setInputUser(fieldValue);
|
|
||||||
|
|
||||||
//ユーザー名が格納できている場合、ログイン名とユーザー名をcorrectFormattedValueに代入する
|
|
||||||
if(!users){
|
|
||||||
event.record[fieldCode]['error']="ユーザー選択に、挿入しようとしたユーザー情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。";
|
|
||||||
throw new Error("ユーザー選択に、挿入しようとしたユーザー情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}else{
|
|
||||||
correctFormattedValue=[{ code: fieldValue, name: users }];
|
|
||||||
}
|
|
||||||
|
|
||||||
//組織情報フィードに挿入しようとした値が適切な場合、correctFormattedValueに代入する
|
|
||||||
}else if(fieldType === "ORGANIZATION_SELECT"){
|
|
||||||
//挿入する値が組織情報から見つかれば、組織名を格納
|
|
||||||
let organizations=await this.setInputOrganization(fieldValue);
|
|
||||||
|
|
||||||
//組織名が格納できている場合、組織コードと組織名をcorrectFormattedValueに代入する
|
|
||||||
if(!organizations){
|
|
||||||
event.record[fieldCode]['error']="組織選択フィールドに、挿入しようとした組織情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。";
|
|
||||||
throw new Error("組織選択フィールドに、挿入しようとした組織情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}else{
|
|
||||||
correctFormattedValue=[{ code: fieldValue, name: organizations}];
|
|
||||||
}
|
|
||||||
|
|
||||||
//グループ情報フィードに挿入しようとした値が適切な場合、correctFormattedValueに代入する
|
|
||||||
}else if(fieldType === "GROUP_SELECT"){
|
|
||||||
//挿入する値がグループ情報から見つかれば、グループ名を格納
|
|
||||||
let groups=await this.setInputGroup(fieldValue);
|
|
||||||
|
|
||||||
//グループ名が格納できている場合、グループコードとグループ名をcorrectFormattedValueに代入する
|
|
||||||
if(!groups){
|
|
||||||
event.record[fieldCode]['error']="グループ選択フィールドに、挿入しようとしたグループ情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。";
|
|
||||||
throw new Error("グループ選択フィールドに、挿入しようとしたグループ情報は見つかりませんでした。「値を挿入する」コンポーネントの処理を中断しました。");
|
|
||||||
}else{
|
|
||||||
correctFormattedValue=[{ code: fieldValue, name: groups}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//保存成功イベントの場合、kintone async/await による非同期処理でフィールドに値を挿入する
|
|
||||||
if(!event.type.includes('success')){
|
|
||||||
|
|
||||||
//条件式の結果がtrueかつ挿入する値が変換できた場合、フィールド(ラジオボタン・ドロップダウン・チェックボックス・複数選択・文字列一行・文字列複数行・リッチエディタ・数値・日付・日時・時刻)にセット
|
|
||||||
if(conditionResult && (correctFormattedValue || correctValues)){
|
|
||||||
//条件式の結果がtureかつ、値を正しい形式に変換できた場合、フィールドに値をセットする
|
|
||||||
if(correctFormattedValue){
|
|
||||||
event.record[fieldCode].value = correctFormattedValue;
|
|
||||||
//条件式の結果がtureかつ、値を正しい形式(配列)に変換できた場合、フィールドに値(配列)をセットする
|
|
||||||
}else if(correctValues.length > 0){
|
|
||||||
event.record[fieldCode].value = correctValues;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}else{
|
|
||||||
//kintone async/await による非同期処理(保存成功時イベントREST API処理時)
|
|
||||||
async function updateRecord(fieldCode:string,event:any,insertValue:any) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var updatedRecord = {
|
|
||||||
app: event.appId,
|
|
||||||
id: event.recordId,
|
|
||||||
record: {[fieldCode]:{"value":insertValue}}
|
|
||||||
};
|
|
||||||
kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', updatedRecord, (resp) => {
|
|
||||||
resolve(resp);
|
|
||||||
}, (error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//条件式の結果がtrueかつ挿入する値が変換できた場合、フィールド(ラジオボタン・ドロップダウン・チェックボックス・複数選択・文字列一行・文字列複数行・リッチエディタ・数値・日付・日時・時刻)にセット
|
|
||||||
if(conditionResult && (correctFormattedValue || correctValues)){
|
|
||||||
//条件式の結果がtureかつ、値を正しい形式に変換できた場合、フィールドに値をセットする
|
|
||||||
if(correctFormattedValue){
|
|
||||||
event.record[fieldCode].value = correctFormattedValue;
|
|
||||||
//条件式の結果がtureかつ、値を正しい形式(配列)に変換できた場合、フィールドに値(配列)をセットする
|
|
||||||
}else if(correctValues.length > 0){
|
|
||||||
event.record[fieldCode].value = correctValues;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//kintone async/await による非同期処理(レコード更新)
|
|
||||||
await updateRecord(fieldCode,event,correctFormattedValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
result= {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}catch(error:any){
|
|
||||||
event.record;
|
|
||||||
event.error=error.message;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext=true;//次のノードは処理を続ける
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 InsertValueAction();
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IField,IAction,IActionResult, IActionNode, IActionProperty,IContext,IVarName} from "../types/ActionTypes";
|
|
||||||
//右UI画面propertyのname:型:
|
|
||||||
interface ILoginUserGetterProps{
|
|
||||||
//変数名にセット:値がオブジェクトの形式
|
|
||||||
verName:IVarName;
|
|
||||||
}
|
|
||||||
//IActionインタフェースを実装する新しいクラスActionを作成:
|
|
||||||
export class LoginUserGetterAction implements IAction{
|
|
||||||
name: string;
|
|
||||||
//importから導入顕示定義
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
//上方のinterface Propsから、props受ける属性を定義:
|
|
||||||
props:ILoginUserGetterProps;
|
|
||||||
//関数定義に必要な類名を構築:
|
|
||||||
constructor(){
|
|
||||||
//pgAdminDBに登録したアクション名(name/subtitle)一致する必要がある:
|
|
||||||
this.name="ログインユーザー取得";
|
|
||||||
this.actionProps=[];
|
|
||||||
this.register();
|
|
||||||
//プロパティ属性初期化:
|
|
||||||
this.props={
|
|
||||||
verName:{name:''}
|
|
||||||
}
|
|
||||||
//リセット上記登録表:
|
|
||||||
this.register();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* アクションの処理を実装する
|
|
||||||
* @param actionNode アクションノード
|
|
||||||
* @param event Kintoneのイベント
|
|
||||||
* @param context コンテキスト(レコード、変数情報を持っている)
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
//非同期処理ある関数下のある属性:
|
|
||||||
async process(actionNode:IActionNode,event:any,context:IContext):Promise<IActionResult> {
|
|
||||||
let result={
|
|
||||||
canNext:true,
|
|
||||||
result:false
|
|
||||||
};
|
|
||||||
try{
|
|
||||||
//属性設定を取得する:
|
|
||||||
this.actionProps=actionNode.actionProps;
|
|
||||||
//プロパティ設定のデータ型は必要な情報含めますか?全部不存在時return:
|
|
||||||
if (!('verName' in actionNode.ActionValue)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
//既定のプロパティのインタフェースへ変換:
|
|
||||||
this.props = actionNode.ActionValue as ILoginUserGetterProps;
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
if(this.props.verName&&this.props.verName.name!==''){
|
|
||||||
context.variables[this.props.verName.name]=kintone.getLoginUser();
|
|
||||||
// window.alert("変数名"+this.props.verName.name+"の値は"+context.variables[this.props.verName.name]+"です");
|
|
||||||
// event.record["変数運用先のユーザ選択フィールド"].value= [{code:context.variables[this.props.verName.name].code}];
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
result= {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
}catch(error:any){;
|
|
||||||
event.error=error.message;
|
|
||||||
return {
|
|
||||||
canNext:false,
|
|
||||||
result:false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name]=this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new LoginUserGetterAction();
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction, IActionResult, IActionNode, IActionProperty, IField } from "../types/ActionTypes";
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface IMailCheckProps {
|
|
||||||
field: IField;//チェックするフィールドの対象
|
|
||||||
message: string;//エラーメッセージ
|
|
||||||
emailCheck:string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* メールアドレスチェックアクション
|
|
||||||
*/
|
|
||||||
export class MailCheckAction implements IAction {
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props: IMailCheckProps;
|
|
||||||
constructor() {
|
|
||||||
this.name = "メールアドレスチェック";
|
|
||||||
this.actionProps = [];
|
|
||||||
this.props = {
|
|
||||||
field: { code: '' },
|
|
||||||
message: '',
|
|
||||||
emailCheck:''
|
|
||||||
}
|
|
||||||
//アクションを登録する
|
|
||||||
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 (!('field' in actionNode.ActionValue) && !('message' in actionNode.ActionValue) && !('emailCheck' in actionNode.ActionValue)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
this.props = actionNode.ActionValue as IMailCheckProps;
|
|
||||||
//条件式の計算結果を取得
|
|
||||||
const record = event.record;
|
|
||||||
const value = record[this.props.field.code].value;
|
|
||||||
|
|
||||||
if (this.props.emailCheck === '厳格') {
|
|
||||||
if (!/^[a-zA-Z0-9_-¥.]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(value)) {
|
|
||||||
record[this.props.field.code].error = this.props.message;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
record[this.props.field.code].error = null;
|
|
||||||
}
|
|
||||||
} else if (this.props.emailCheck === 'ゆるめ') {
|
|
||||||
if (!/^[^@]+@[^@]+$/.test(value)) {
|
|
||||||
record[this.props.field.code].error = this.props.message;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
record[this.props.field.code].error = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = {
|
|
||||||
canNext: true,
|
|
||||||
result: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
event.error = error;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext = false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
new MailCheckAction();
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction,IActionResult, IActionNode, IActionProperty, IField} from "../types/ActionTypes";
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface IRegularCheckProps{
|
|
||||||
field:IField;//チェックするフィールドの対象
|
|
||||||
message:string;//エラーメッセージ
|
|
||||||
regExpression:string;//正規表現式
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 正規表現チェックアクション
|
|
||||||
*/
|
|
||||||
export class RegularCheckAction implements IAction{
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props:IRegularCheckProps;
|
|
||||||
constructor(){
|
|
||||||
this.name="正規表現チェック";
|
|
||||||
this.actionProps=[];
|
|
||||||
this.props={
|
|
||||||
field:{code:''},
|
|
||||||
message:'',
|
|
||||||
regExpression:''
|
|
||||||
}
|
|
||||||
//アクションを登録する
|
|
||||||
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 (!('field' in actionNode.ActionValue) && !('message' in actionNode.ActionValue) && !('regExpression'in actionNode.ActionValue)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
this.props = actionNode.ActionValue as IRegularCheckProps;
|
|
||||||
//条件式の計算結果を取得
|
|
||||||
const record = event.record;
|
|
||||||
const value = record[this.props.field.code].value;
|
|
||||||
const regex = new RegExp(this.props.regExpression);
|
|
||||||
if(!regex.test(value)){
|
|
||||||
record[this.props.field.code].error=this.props.message;
|
|
||||||
}else{
|
|
||||||
result= {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}catch(error){
|
|
||||||
event.error=error;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext=false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name]=this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
new RegularCheckAction();
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IField, IAction,IActionResult, IActionNode, IActionProperty } from "../types/ActionTypes";
|
|
||||||
import { Formatter } from "../util/format";
|
|
||||||
|
|
||||||
//右UI画面propertyのname:型:
|
|
||||||
interface IStringJoinProps{
|
|
||||||
//保存先フィールド
|
|
||||||
saveField:IField;
|
|
||||||
//結合元フィールド1
|
|
||||||
joinField1:IField;
|
|
||||||
//結合元フィールド2
|
|
||||||
joinField2:IField;
|
|
||||||
//区切り文字
|
|
||||||
delimiter:string;
|
|
||||||
}
|
|
||||||
|
|
||||||
//IActionインタフェースを実装する新しいクラスActionを作成:
|
|
||||||
export class StringJoinAction implements IAction{
|
|
||||||
name: string;
|
|
||||||
//importから導入顕示定義
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
//上方のinterface Propsから、props受ける属性を定義:
|
|
||||||
props:IStringJoinProps;
|
|
||||||
//関数定義に必要な類名を構築:
|
|
||||||
constructor(){
|
|
||||||
//pgAdminDBに登録したアクション名(name/subtitle)一致する必要がある:
|
|
||||||
this.name="文字結合";
|
|
||||||
this.actionProps=[];
|
|
||||||
this.register();
|
|
||||||
//プロパティ属性初期化:
|
|
||||||
this.props={
|
|
||||||
saveField:{code:''},
|
|
||||||
joinField1:{code:''},
|
|
||||||
joinField2:{code:''},
|
|
||||||
delimiter:''
|
|
||||||
}
|
|
||||||
//リセット上記登録表:
|
|
||||||
this.register();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* アクションの処理を実装する
|
|
||||||
* @param actionNode アクションノード
|
|
||||||
* @param event Kintoneのイベント
|
|
||||||
* @param context コンテキスト(レコード、変数情報を持っている)
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
//非同期処理ある関数下のある属性:
|
|
||||||
async process(actionNode:IActionNode,event:any):Promise<IActionResult> {
|
|
||||||
let result={
|
|
||||||
//後継処理不可:
|
|
||||||
canNext:false,
|
|
||||||
result:false
|
|
||||||
};
|
|
||||||
try{
|
|
||||||
//属性設定を取得する:
|
|
||||||
this.actionProps=actionNode.actionProps;
|
|
||||||
//プロパティ設定のデータ型は必要な情報含めますか?全部不存在時return:
|
|
||||||
if (!('saveField' in actionNode.ActionValue) && !('joinField1' in actionNode.ActionValue) && !('joinField2' in actionNode.ActionValue)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
//既定のプロパティのインタフェースへ変換:
|
|
||||||
this.props = actionNode.ActionValue as IStringJoinProps;
|
|
||||||
const record = event.record;
|
|
||||||
|
|
||||||
//kintoneフィールドタイプ取得:
|
|
||||||
const joinField1type=this.props.joinField1.type;
|
|
||||||
const joinField2type=this.props.joinField2.type;
|
|
||||||
const saveFieldtype=this.props.saveField.type;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// //保存先フィールドは文字列フィールドではない場合、エラー発生:
|
|
||||||
if(!(saveFieldtype==='SINGLE_LINE_TEXT'||saveFieldtype==='MULTI_LINE_TEXT'||saveFieldtype==='RICH_TEXT')){
|
|
||||||
event.error='[エラーメッセージ]:結合保存先対応不可。結合しません';
|
|
||||||
if (event.type.includes('success')){
|
|
||||||
window.alert("[windows alert]:"+event.error);
|
|
||||||
}
|
|
||||||
result = {
|
|
||||||
canNext: false,
|
|
||||||
result: false
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//値取得方法定義:
|
|
||||||
function getValue(value:string,type:string|undefined,fieldCode:string,event:any){
|
|
||||||
if(event.record[fieldCode]?.value===undefined||event.record[fieldCode]?.value===null){
|
|
||||||
event.record[fieldCode].value='';
|
|
||||||
}
|
|
||||||
//作成者、更新者:
|
|
||||||
if(type==='CREATOR'||type==='MODIFIER'){
|
|
||||||
value = event.record[fieldCode]?.value.name;
|
|
||||||
//日時、作成日時、更新日時:
|
|
||||||
}else if(type==='DATETIME'||type==='CREATED_TIME'||type==='UPDATED_TIME'){
|
|
||||||
if(event.record[fieldCode]?.value!==undefined && event.record[fieldCode]?.value!==''){
|
|
||||||
value = Formatter.dateFormat(new Date(event.record[fieldCode]?.value),'yyyy-MM-dd HH:mm');
|
|
||||||
}else{
|
|
||||||
value=event.record[fieldCode]?.value;
|
|
||||||
}
|
|
||||||
//ユーザ選択、組織選択、グループ選択、添付ファイル名、作業者、カテゴリー:
|
|
||||||
}else if(type==='USER_SELECT'||type==='ORGANIZATION_SELECT'||type==='GROUP_SELECT'||type==='FILE'||type==='STATUS_ASSIGNEE'){
|
|
||||||
if(event.record[fieldCode]?.value===undefined || event.record[fieldCode]?.value===''){
|
|
||||||
value = event.record[fieldCode]?.value;
|
|
||||||
}else{
|
|
||||||
const mototext=event.record[fieldCode]?.value;
|
|
||||||
let arr=[];
|
|
||||||
for(let i=0;i<mototext.length;i++){
|
|
||||||
arr.push(mototext[i].name);
|
|
||||||
}
|
|
||||||
//配列要素を,で連結して文字列を作成:
|
|
||||||
value=arr.join();
|
|
||||||
}
|
|
||||||
//カテゴリー、チェックボックス、複数選択:
|
|
||||||
}else if(type==='CATEGORY'||type==='CHECK_BOX'||type==='MULTI_SELECT'){
|
|
||||||
if(event.record[fieldCode]?.value===undefined || event.record[fieldCode]?.value===''){
|
|
||||||
value = event.record[fieldCode]?.value;
|
|
||||||
}else{
|
|
||||||
const mototext=event.record[fieldCode]?.value;
|
|
||||||
let arr=[];
|
|
||||||
for(let i=0;i<mototext.length;i++){
|
|
||||||
arr.push(mototext[i]);
|
|
||||||
}
|
|
||||||
//配列要素を,で連結して文字列を作成:
|
|
||||||
value=arr.join();
|
|
||||||
}
|
|
||||||
//詳細画面プロセス実行後のステータス:
|
|
||||||
}else if(type==='STATUS'&&event.type.includes('process')){
|
|
||||||
value = event.nextStatus.value;
|
|
||||||
}else{
|
|
||||||
value = event.record[fieldCode]?.value;
|
|
||||||
}
|
|
||||||
if (value===undefined || value===null){
|
|
||||||
value='';
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
//値取得方法呼出:
|
|
||||||
let joinValue1:string='';
|
|
||||||
joinValue1=getValue(joinValue1,joinField1type,this.props.joinField1.code,event);
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
let joinValue2:string='';
|
|
||||||
joinValue2=getValue(joinValue2,joinField2type,this.props.joinField2.code,event);
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
const conString = this.props.delimiter;
|
|
||||||
let saveValue:string='';
|
|
||||||
//前後結合元が空白なら区切り文字も空白にする(例:1-8の8無いなら1。1-8の1無いなら8。1空白8の8無いなら1。結合元全部空白なら全部空白):
|
|
||||||
if(joinValue1===''&&joinValue2===''){
|
|
||||||
saveValue='';
|
|
||||||
}else if(joinValue1===''&&joinValue2!==''){
|
|
||||||
saveValue=joinValue2;
|
|
||||||
}else if(joinValue2===''&&joinValue1!==''){
|
|
||||||
saveValue=joinValue1;
|
|
||||||
}else if(joinValue1!==''&&joinValue2!==''){
|
|
||||||
saveValue=`${joinValue1}${conString}${joinValue2}`
|
|
||||||
}
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//新規/更新/一覧保存成功後の以外のeventでPUT使用しない:
|
|
||||||
if (!event.type.includes('success')){
|
|
||||||
//保存先フィールドに値セット:
|
|
||||||
record[this.props.saveField.code].value=saveValue;
|
|
||||||
//window.alert("文字結合行いました。"+this.props.joinField1.name+":"+joinValue1+","+this.props.joinField2.name+":"+joinValue2+"。");
|
|
||||||
}else{
|
|
||||||
//kintone async/await による非同期処理(成功イベントREST API処理時):
|
|
||||||
async function updateRecord(fieldCode:string,event:any) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var updatedRecord = {
|
|
||||||
app: event.appId,
|
|
||||||
id: event.recordId,
|
|
||||||
record: {[fieldCode]:{"value":saveValue}}
|
|
||||||
};
|
|
||||||
kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', updatedRecord, (resp) => {
|
|
||||||
resolve(resp);
|
|
||||||
}, (error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//kintone保存先フィールド存在確認:
|
|
||||||
record[this.props.saveField.code].value=saveValue;
|
|
||||||
//kintone async/await による非同期処理:
|
|
||||||
await updateRecord(this.props.saveField.code,event);
|
|
||||||
//一覧画面更新成功後手動リロードください:
|
|
||||||
if (event.type.includes('index')){
|
|
||||||
//window.alert("文字結合行いました。"+this.props.joinField1.name+":"+joinValue1+","+this.props.joinField2.name+":"+joinValue2+"。一覧画面更新成功後自動リロードしません。必要に応じて手動リロードください。");
|
|
||||||
window.alert("文字結合には、一覧画面更新成功後自動リロードしません。必要に応じて手動リロードください。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
result= {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}catch(error){;
|
|
||||||
if (event.type.includes('success')){
|
|
||||||
window.alert("[windows alert]:処理中異常が発生しました。結合しません。システム担当者へお問合せください。errorメッセージ:"+error)
|
|
||||||
}
|
|
||||||
event.error="[エラーメッセージ]:処理中異常が発生しました。結合しません。システム担当者へお問合せください。errorメッセージ:"+error;
|
|
||||||
return {
|
|
||||||
canNext:false,
|
|
||||||
result:false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name]=this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new StringJoinAction();
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction,IActionResult, IActionNode, IActionProperty, IField, IContext } from "../types/ActionTypes";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface FullWidthProps{
|
|
||||||
field:IField
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 全角チェックアクション
|
|
||||||
*/
|
|
||||||
export class FullWidthAction implements IAction{
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props:FullWidthProps;
|
|
||||||
constructor(){
|
|
||||||
this.name="全角チェック"; /* pgadminのnameと同様 */
|
|
||||||
this.actionProps=[];
|
|
||||||
this.props={
|
|
||||||
field:{code:''}
|
|
||||||
}
|
|
||||||
//アクションを登録する
|
|
||||||
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) ) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
this.props = actionNode.ActionValue as FullWidthProps;
|
|
||||||
//条件式の計算結果を取得
|
|
||||||
const record = event.record;
|
|
||||||
const value = record[this.props.field.code]?.value;
|
|
||||||
//条件分岐
|
|
||||||
//未入力時は何も処理をせず終了
|
|
||||||
if(value===undefined || value===''){
|
|
||||||
record[this.props.field.code].error=null;
|
|
||||||
}
|
|
||||||
//半角が含まれていた場合resultがfalse
|
|
||||||
if(!this.containsFullWidthChars(value) && !(value === undefined || value ==='')){
|
|
||||||
//エラー時に出力される文字設定
|
|
||||||
record[this.props.field.code].error="半角が含まれています";
|
|
||||||
//次の処理を中止する値設定
|
|
||||||
result.canNext=false;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
record[this.props.field.code].error=null;
|
|
||||||
}
|
|
||||||
//resultプロパティ指定
|
|
||||||
result= {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
|
|
||||||
//例外処理
|
|
||||||
}catch(error){
|
|
||||||
event.error=error;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext=false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 全て全角の文字列の場合はtrue、そうでない場合はfalse
|
|
||||||
containsFullWidthChars(text: string): boolean {
|
|
||||||
|
|
||||||
// 半角英数字カナ記号を除外
|
|
||||||
//全角かどうか
|
|
||||||
const checkRegex="^[^\x01-\x7E\uFF61-\uFF9F]+$";
|
|
||||||
|
|
||||||
//正規表現オブジェクト生成
|
|
||||||
const fullWidthRegex = new RegExp(checkRegex);
|
|
||||||
|
|
||||||
//正規表現チェック
|
|
||||||
return fullWidthRegex.test(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
//戻り値を持たないためvoid型
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name]=this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new FullWidthAction();
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction,IActionResult, IActionNode, IActionProperty, IField, IContext } from "../types/ActionTypes";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface HalfWidthProps{
|
|
||||||
field:IField
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 半角チェックアクション
|
|
||||||
*/
|
|
||||||
export class HalfWidthAction implements IAction{
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props:HalfWidthProps;
|
|
||||||
constructor(){
|
|
||||||
this.name="半角チェック"; /* pgadminのnameと同様 */
|
|
||||||
this.actionProps=[];
|
|
||||||
this.props={
|
|
||||||
field:{code:''}
|
|
||||||
}
|
|
||||||
//アクションを登録する
|
|
||||||
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) ) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
this.props = actionNode.ActionValue as HalfWidthProps;
|
|
||||||
//条件式の計算結果を取得
|
|
||||||
const record = event.record;
|
|
||||||
const value = record[this.props.field.code]?.value;
|
|
||||||
//条件分岐
|
|
||||||
//未入力時は何も処理をせず終了
|
|
||||||
if(value===undefined || value===''){
|
|
||||||
record[this.props.field.code].error=null;
|
|
||||||
}
|
|
||||||
//全角が含まれていた場合保存処理中止(エラー処理)
|
|
||||||
if(!this.containsHalfWidthChars(value)){
|
|
||||||
//エラー時に出力される文字設定
|
|
||||||
record[this.props.field.code].error="全角が含まれています";
|
|
||||||
//次の処理を中止する値設定
|
|
||||||
result.canNext=false;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
record[this.props.field.code].error=null;
|
|
||||||
}
|
|
||||||
//半角の場合問題なく実行
|
|
||||||
//resultプロパティ指定
|
|
||||||
result= {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
//例外処理
|
|
||||||
}catch(error){
|
|
||||||
event.error=error;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext=false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 全て全角の文字列の場合はtrue、そうでない場合はfalse
|
|
||||||
containsHalfWidthChars(text: string): boolean {
|
|
||||||
|
|
||||||
const checkRegex = "^[\x01-\x7E\uFF61-\uFF9F]+$";
|
|
||||||
//正規表現オブジェクト生成
|
|
||||||
const halfWidthRegex = new RegExp(checkRegex);
|
|
||||||
|
|
||||||
//正規表現チェック
|
|
||||||
return halfWidthRegex.test(text);
|
|
||||||
}
|
|
||||||
//戻り値を持たないためvoid型
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name]=this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new HalfWidthAction();
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import { actionAddins } from ".";
|
|
||||||
import { IAction,IActionResult, IActionNode, IActionProperty, IField, IContext, IVarName} from "../types/ActionTypes";
|
|
||||||
/**
|
|
||||||
* アクションの属性定義
|
|
||||||
*/
|
|
||||||
interface IGetValueProps{
|
|
||||||
field:IField;//チェックするフィールドの対象
|
|
||||||
verName:IVarName;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 正規表現チェックアクション
|
|
||||||
*/
|
|
||||||
export class GetValueAciton implements IAction{
|
|
||||||
name: string;
|
|
||||||
actionProps: IActionProperty[];
|
|
||||||
props:IGetValueProps;
|
|
||||||
constructor(){
|
|
||||||
this.name="値を取得する";
|
|
||||||
this.actionProps=[];
|
|
||||||
this.props={
|
|
||||||
field:{code:''},
|
|
||||||
verName:{name:''}
|
|
||||||
}
|
|
||||||
//アクションを登録する
|
|
||||||
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) && !('verName' in actionNode.ActionValue)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props = actionNode.ActionValue as IGetValueProps;
|
|
||||||
//条件式の計算結果を取得
|
|
||||||
const record = event.record;
|
|
||||||
const value = record[this.props.field.code].value;
|
|
||||||
if(this.props.verName && this.props.verName.name!==''){
|
|
||||||
context.variables[this.props.verName.name] = value;
|
|
||||||
}
|
|
||||||
result = {
|
|
||||||
canNext:true,
|
|
||||||
result:true
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}catch(error){
|
|
||||||
event.error=error;
|
|
||||||
console.error(error);
|
|
||||||
result.canNext=false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
register(): void {
|
|
||||||
actionAddins[this.name]=this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
new GetValueAciton();
|
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
// export const sum = (a: number, b: number) => {
|
||||||
|
// if ('development' === process.env.NODE_ENV) {
|
||||||
|
// console.log('boop');
|
||||||
|
// }
|
||||||
|
// return a + b;
|
||||||
|
// };
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import { ActionProcess } from './types/action-process';
|
import { ActionProcess } from './types/action-process';
|
||||||
import { ActionFlow } from './types/ActionTypes';
|
import { ActionFlow } from './types/ActionTypes';
|
||||||
@@ -42,12 +48,7 @@ $(function (){
|
|||||||
const flow=ActionFlow.fromJSON(flowinfo.content);
|
const flow=ActionFlow.fromJSON(flowinfo.content);
|
||||||
if(flow!==undefined){
|
if(flow!==undefined){
|
||||||
const process = new ActionProcess(event.type,flow,event);
|
const process = new ActionProcess(event.type,flow,event);
|
||||||
process.exec().then((res)=>{
|
process.exec();
|
||||||
const record = event.record;
|
|
||||||
kintone.app.record.set({record});
|
|
||||||
}).catch((err)=>{
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return event;
|
return event;
|
||||||
});
|
});
|
||||||
@@ -63,7 +64,7 @@ $(function (){
|
|||||||
await process.exec();
|
await process.exec();
|
||||||
}
|
}
|
||||||
const record = event.record;
|
const record = event.record;
|
||||||
kintone.app.record.set({record});
|
kintone.app.record.set({record})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,13 +89,6 @@ export interface IField{
|
|||||||
name?:string;
|
name?:string;
|
||||||
code:string;
|
code:string;
|
||||||
type?:string;
|
type?:string;
|
||||||
required?:boolean;
|
|
||||||
options?:string;
|
|
||||||
}
|
|
||||||
//変数のインターフェース
|
|
||||||
export interface IVarName{
|
|
||||||
name:string;
|
|
||||||
fields?:IVarName[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -347,9 +347,6 @@ export class ConditionTree {
|
|||||||
if(!object || typeof object!=="object"){
|
if(!object || typeof object!=="object"){
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
if("sharedText" in object){
|
|
||||||
return object.sharedText;
|
|
||||||
}
|
|
||||||
if("label" in object){
|
if("label" in object){
|
||||||
return object.label;
|
return object.label;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,240 +0,0 @@
|
|||||||
// src/types/field.d.ts
|
|
||||||
|
|
||||||
export enum FieldType {
|
|
||||||
CALC = "CALC",
|
|
||||||
CATEGORY = "CATEGORY",
|
|
||||||
CHECK_BOX = "CHECK_BOX",
|
|
||||||
CREATED_TIME = "CREATED_TIME",
|
|
||||||
CREATOR = "CREATOR",
|
|
||||||
DATE = "DATE",
|
|
||||||
DATETIME = "DATETIME",
|
|
||||||
DROP_DOWN = "DROP_DOWN",
|
|
||||||
FILE = "FILE",
|
|
||||||
GROUP = "GROUP",
|
|
||||||
GROUP_SELECT = "GROUP_SELECT",
|
|
||||||
LINK = "LINK",
|
|
||||||
MODIFIER = "MODIFIER",
|
|
||||||
MULTI_LINE_TEXT = "MULTI_LINE_TEXT",
|
|
||||||
MULTI_SELECT = "MULTI_SELECT",
|
|
||||||
NUMBER = "NUMBER",
|
|
||||||
ORGANIZATION_SELECT = "ORGANIZATION_SELECT",
|
|
||||||
RADIO_BUTTON = "RADIO_BUTTON",
|
|
||||||
RECORD_NUMBER = "RECORD_NUMBER",
|
|
||||||
REFERENCE_TABLE = "REFERENCE_TABLE",
|
|
||||||
RICH_TEXT = "RICH_TEXT",
|
|
||||||
SINGLE_LINE_TEXT = "SINGLE_LINE_TEXT",
|
|
||||||
STATUS = "STATUS",
|
|
||||||
STATUS_ASSIGNEE = "STATUS_ASSIGNEE",
|
|
||||||
SUBTABLE = "SUBTABLE",
|
|
||||||
TIME = "TIME",
|
|
||||||
UPDATED_TIME = "UPDATED_TIME",
|
|
||||||
USER_SELECT = "USER_SELECT"
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CalcFormat{
|
|
||||||
NUMBER = "NUMBER",
|
|
||||||
NUMBER_DIGIT = "NUMBER_DIGIT",
|
|
||||||
DATETIME = "DATETIME",
|
|
||||||
DATE = "DATE",
|
|
||||||
TIME = "TIME",
|
|
||||||
HOUR_MINUTE= "HOUR_MINUTE",
|
|
||||||
DAY_HOUR_MINUTE= "DAY_HOUR_MINUTE"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FieldForm {
|
|
||||||
code: string;
|
|
||||||
type: FieldType;
|
|
||||||
required?: boolean;
|
|
||||||
noLabel?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OptionForm{
|
|
||||||
index: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EntityForm {
|
|
||||||
code: string;
|
|
||||||
type: 'USER' | 'ORGANIZATION' | 'GROUP' | 'FUNCTION';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CalcFieldForm extends FieldForm {
|
|
||||||
unit?: string;
|
|
||||||
expression?: string;
|
|
||||||
format?:CalcFormat;
|
|
||||||
unitPosition?: 'BEFORE' | 'AFTER';
|
|
||||||
displayScale?: string;
|
|
||||||
hideExpression?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CategoryFieldForm extends FieldForm {
|
|
||||||
enabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DateFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string;
|
|
||||||
unique?: boolean;
|
|
||||||
defaultNowValue?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DatetimeFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string;
|
|
||||||
unique?: boolean;
|
|
||||||
defaultNowValue?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DecimalFieldForm extends FieldForm {
|
|
||||||
maxValue?: string;
|
|
||||||
defaultValue?: string;
|
|
||||||
unitPosition?: 'BEFORE' | 'AFTER';
|
|
||||||
minValue?: string;
|
|
||||||
unit?: string;
|
|
||||||
unique?: boolean;
|
|
||||||
displayScale?: string;
|
|
||||||
digit?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EditorFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EntityForm {
|
|
||||||
code: string;
|
|
||||||
type: 'USER' | 'ORGANIZATION' | 'GROUP' | 'FUNCTION';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FieldMappingForm {
|
|
||||||
relatedField: string;
|
|
||||||
field: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FileFieldForm extends FieldForm {
|
|
||||||
thumbnailSize?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupFieldForm extends FieldForm {
|
|
||||||
openGroup?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupSelectFieldForm extends FieldForm {
|
|
||||||
entities: EntityForm[];
|
|
||||||
defaultValue?: EntityForm[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LinkFieldForm extends FieldForm {
|
|
||||||
protocol?: 'WEB' | 'CALL' | 'MAIL';
|
|
||||||
defaultValue?: string;
|
|
||||||
minLength?: string;
|
|
||||||
unique?: boolean;
|
|
||||||
maxLength?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LookupFieldForm extends FieldForm {
|
|
||||||
lookup: {
|
|
||||||
lookupPickerFields: string[];
|
|
||||||
relatedKeyField: string;
|
|
||||||
relatedApp: {
|
|
||||||
app: string;
|
|
||||||
code: string;
|
|
||||||
};
|
|
||||||
fieldMappings: FieldMappingForm[];
|
|
||||||
sort?: string;
|
|
||||||
filterCond?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModifiedAtFieldForm extends FieldForm {}
|
|
||||||
|
|
||||||
export interface ModifierFieldForm extends FieldForm {}
|
|
||||||
|
|
||||||
export interface MultipleCheckFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string[];
|
|
||||||
options: {
|
|
||||||
[key: string]: OptionForm;
|
|
||||||
};
|
|
||||||
align?: 'HORIZONTAL' | 'VERTICAL';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MultipleLineTextFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MultipleSelectFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string[];
|
|
||||||
options: {
|
|
||||||
[key: string]: OptionForm;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface OrganizationSelectFieldForm extends FieldForm {
|
|
||||||
entities: EntityForm[];
|
|
||||||
defaultValue?: EntityForm[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RecordIdFieldForm extends FieldForm {}
|
|
||||||
|
|
||||||
export interface ReferenceTableFieldForm extends FieldForm {
|
|
||||||
referenceTable: {
|
|
||||||
condition: {
|
|
||||||
relatedField: string;
|
|
||||||
field: string;
|
|
||||||
};
|
|
||||||
size?: string;
|
|
||||||
relatedApp: {
|
|
||||||
app: string;
|
|
||||||
code: string;
|
|
||||||
};
|
|
||||||
sort?: string;
|
|
||||||
filterCond?: string;
|
|
||||||
displayFields: string[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SingleCheckFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string;
|
|
||||||
options: {
|
|
||||||
[key: string]: OptionForm;
|
|
||||||
};
|
|
||||||
align?: 'HORIZONTAL' | 'VERTICAL';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SingleLineTextFieldForm extends FieldForm {
|
|
||||||
expression?: string;
|
|
||||||
defaultValue?: string;
|
|
||||||
minLength?: string;
|
|
||||||
unique?: boolean;
|
|
||||||
maxLength?: string;
|
|
||||||
hideExpression?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SingleSelectFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string;
|
|
||||||
options: {
|
|
||||||
[key: string]: OptionForm;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StatusAssigneeFieldForm extends FieldForm {
|
|
||||||
enabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StatusFieldForm extends FieldForm {
|
|
||||||
enabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TableForm extends FieldForm {
|
|
||||||
fields: {
|
|
||||||
[key: string]: CalcFieldForm | CategoryFieldForm | DateFieldForm | DatetimeFieldForm | DecimalFieldForm | EditorFieldForm | FileFieldForm | GroupFieldForm | GroupSelectFieldForm | LinkFieldForm | LookupFieldForm | ModifiedAtFieldForm | ModifierFieldForm | MultipleCheckFieldForm | MultipleLineTextFieldForm | MultipleSelectFieldForm | OrganizationSelectFieldForm | RecordIdFieldForm | ReferenceTableFieldForm | SingleCheckFieldForm | SingleLineTextFieldForm | SingleSelectFieldForm | StatusAssigneeFieldForm | StatusFieldForm | TableForm | TimeFieldForm | UserSelectFieldForm;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TimeFieldForm extends FieldForm {
|
|
||||||
defaultValue?: string;
|
|
||||||
defaultNowValue?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserSelectFieldForm extends FieldForm {
|
|
||||||
entities: EntityForm[];
|
|
||||||
defaultValue?: EntityForm[];
|
|
||||||
}
|
|
||||||
@@ -7,20 +7,7 @@ import '../actions/error-show';
|
|||||||
import '../actions/button-add';
|
import '../actions/button-add';
|
||||||
import '../actions/condition-action';
|
import '../actions/condition-action';
|
||||||
import '../actions/data-processing';
|
import '../actions/data-processing';
|
||||||
import '../actions/data-update';
|
import '../actions/data-mapping';
|
||||||
import '../actions/current-field-get';
|
|
||||||
import '../actions/regular-check';
|
|
||||||
import '../actions/mail-check';
|
|
||||||
import '../actions/counter-check';
|
|
||||||
import '../actions/datetime-getter';
|
|
||||||
import '../actions/insert-value';
|
|
||||||
import '../actions/value-getter';
|
|
||||||
import '../actions/string-join';
|
|
||||||
import '../actions/validation-fullwidth';
|
|
||||||
import '../actions/validation-halfwidth';
|
|
||||||
import '../actions/login-user-getter';
|
|
||||||
import '../actions/auto-lookup';
|
|
||||||
|
|
||||||
import { ActionFlow,IActionFlow, IActionResult,IContext } from "./ActionTypes";
|
import { ActionFlow,IActionFlow, IActionResult,IContext } from "./ActionTypes";
|
||||||
|
|
||||||
export class ActionProcess{
|
export class ActionProcess{
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
import { FieldForm,CalcFieldForm,FieldType, CalcFormat} from "../types/FieldLayout";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 集計操作子
|
|
||||||
*/
|
|
||||||
export enum Operator {
|
|
||||||
SUM = "SUM",
|
|
||||||
AVG = "AVG",
|
|
||||||
MAX = "MAX",
|
|
||||||
MIN = "MIN",
|
|
||||||
COUNT = "COUNT",
|
|
||||||
FIRST = "FIRST",
|
|
||||||
LAST = "LAST"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 集計関数
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class Aggregator {
|
|
||||||
private data: string[];
|
|
||||||
private field: FieldForm;
|
|
||||||
|
|
||||||
constructor(data: string[], field: FieldForm) {
|
|
||||||
this.data = data;
|
|
||||||
this.field = field;
|
|
||||||
}
|
|
||||||
|
|
||||||
private toNumberArray(): number[] {
|
|
||||||
const numberArray = this.data.map(item => {
|
|
||||||
const num = Number(item);
|
|
||||||
if (isNaN(num)) {
|
|
||||||
throw new Error(`フィールド「${this.field.code} 」は数値に変換できないため、計算を実行できません。`);
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
});
|
|
||||||
|
|
||||||
return numberArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sum(): number | null {
|
|
||||||
if (this.data.length === 0) return null;
|
|
||||||
const numberArray = this.toNumberArray();
|
|
||||||
return numberArray.reduce((acc, val) => acc + val, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private avg(): number | null {
|
|
||||||
if (this.data.length === 0) return null;
|
|
||||||
const numberArray = this.toNumberArray();
|
|
||||||
const total = numberArray.reduce((acc, val) => acc + val, 0);
|
|
||||||
return total / numberArray.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private max(): number | string | null {
|
|
||||||
if (this.data.length === 0) return null;
|
|
||||||
if (this.field.type === FieldType.NUMBER ) {
|
|
||||||
const numberArray = this.toNumberArray();
|
|
||||||
return Math.max(...numberArray);
|
|
||||||
}
|
|
||||||
if(this.field.type===FieldType.CALC){
|
|
||||||
const calcField = this.field as CalcFieldForm;
|
|
||||||
if(calcField.format===CalcFormat.NUMBER || calcField.format===CalcFormat.NUMBER_DIGIT){
|
|
||||||
const numberArray = this.toNumberArray();
|
|
||||||
return Math.max(...numberArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.data.reduce((max, item) => (item > max ? item : max), this.data[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private min(): number | string | null {
|
|
||||||
if (this.data.length === 0) return null;
|
|
||||||
if (this.field.type === FieldType.NUMBER ) {
|
|
||||||
const numberArray = this.toNumberArray();
|
|
||||||
return Math.min(...numberArray);
|
|
||||||
}
|
|
||||||
if(this.field.type===FieldType.CALC){
|
|
||||||
const calcField = this.field as CalcFieldForm;
|
|
||||||
if(calcField.format===CalcFormat.NUMBER || calcField.format===CalcFormat.NUMBER_DIGIT){
|
|
||||||
const numberArray = this.toNumberArray();
|
|
||||||
return Math.min(...numberArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(this.data===null || this.data.length===0){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this.data.reduce((min, item) => (item < min ? item : min), this.data[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private count(): number {
|
|
||||||
return this.data.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private first(): string | null {
|
|
||||||
return this.data.length ? this.data[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private last(): string | null {
|
|
||||||
return this.data.length ? this.data[this.data.length - 1] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public calculate(operator: Operator): number | string | null {
|
|
||||||
switch (operator) {
|
|
||||||
case Operator.SUM:
|
|
||||||
return this.sum();
|
|
||||||
case Operator.AVG:
|
|
||||||
return this.avg();
|
|
||||||
case Operator.MAX:
|
|
||||||
return this.max();
|
|
||||||
case Operator.MIN:
|
|
||||||
return this.min();
|
|
||||||
case Operator.COUNT:
|
|
||||||
return this.count();
|
|
||||||
case Operator.FIRST:
|
|
||||||
return this.first();
|
|
||||||
case Operator.LAST:
|
|
||||||
return this.last();
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
// vite.config.js
|
// vite.config.js
|
||||||
import { defineConfig, loadEnv } from "vite";
|
import { defineConfig, loadEnv } from "vite";
|
||||||
import checker from "vite-plugin-checker";
|
import checker from "vite-plugin-checker";
|
||||||
// import { libInjectCss } from 'vite-plugin-lib-inject-css';
|
import { libInjectCss } from 'vite-plugin-lib-inject-css';
|
||||||
|
|
||||||
export default ({ mode }) => {
|
export default ({ mode }) => {
|
||||||
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
||||||
const sourcemap = process.env.VITE_SOURCE_MAP=== 'false'?false:process.env.VITE_SOURCE_MAP;
|
|
||||||
// console.log(process.env);
|
|
||||||
return defineConfig({
|
return defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
checker({
|
checker({
|
||||||
typescript: true,
|
typescript: true,
|
||||||
}),
|
}),
|
||||||
// libInjectCss(),
|
libInjectCss(),
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
cssCodeSplit: false,
|
cssCodeSplit: false,
|
||||||
@@ -20,10 +19,10 @@ export default ({ mode }) => {
|
|||||||
input: "src/index.ts", // entry file
|
input: "src/index.ts", // entry file
|
||||||
output: {
|
output: {
|
||||||
entryFileNames: "alc_runtime.js",
|
entryFileNames: "alc_runtime.js",
|
||||||
assetFileNames:'alc_runtime.css'
|
// assetFileNames:'alc_kintone_style.css'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sourcemap: sourcemap,
|
sourcemap: process.env.VITE_SOURCE_MAP,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: process.env.VITE_PORT,
|
port: process.env.VITE_PORT,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
114
sample.json
114
sample.json
@@ -2,7 +2,7 @@
|
|||||||
"id": "",
|
"id": "",
|
||||||
"actionNodes": [
|
"actionNodes": [
|
||||||
{
|
{
|
||||||
"id": "822b5b2c-cfb7-447a-ae77-01bf3a530fae",
|
"id": "c5cd772a-04be-418e-a811-3787f98a2285",
|
||||||
"name": "app.record.create.show",
|
"name": "app.record.create.show",
|
||||||
"title": "レコード追加画面",
|
"title": "レコード追加画面",
|
||||||
"subTitle": "レコード追加画面を表示した後",
|
"subTitle": "レコード追加画面を表示した後",
|
||||||
@@ -12,12 +12,12 @@
|
|||||||
"actionProps": [],
|
"actionProps": [],
|
||||||
"ActionValue": {},
|
"ActionValue": {},
|
||||||
"nextNodeIds": {
|
"nextNodeIds": {
|
||||||
"": "3a762b11-8e15-4353-8148-306bfed6cd5e"
|
"": "1eb097b1-9d08-462e-97b0-6e3e1232edef"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3a762b11-8e15-4353-8148-306bfed6cd5e",
|
"id": "1eb097b1-9d08-462e-97b0-6e3e1232edef",
|
||||||
"name": "ルックアップ更新",
|
"name": "属性UIテスト用",
|
||||||
"inputPoint": "",
|
"inputPoint": "",
|
||||||
"outputPoints": [],
|
"outputPoints": [],
|
||||||
"actionProps": [
|
"actionProps": [
|
||||||
@@ -28,73 +28,85 @@
|
|||||||
"displayName": "表示名",
|
"displayName": "表示名",
|
||||||
"placeholder": "表示を入力してください",
|
"placeholder": "表示を入力してください",
|
||||||
"hint": "",
|
"hint": "",
|
||||||
"modelValue": "元データ更新時にルックアップ先が同期する"
|
"modelValue": "属性UIテスト用"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"component": "AppFieldSelect",
|
"component": "AppFieldSelect",
|
||||||
"props": {
|
"props": {
|
||||||
"displayName": "更新先選択",
|
"displayName": "フィールド選択(複数)",
|
||||||
"modelValue": {
|
"modelValue": {
|
||||||
"app": {
|
"app": {
|
||||||
"id": "247",
|
"id": "64",
|
||||||
"name": "商品購買記録",
|
"name": "日報テスト",
|
||||||
"description": "",
|
"description": "日々の業務内容、報告事項、所感などを記載していくアプリです。\n記録を行うだけでなく、あとからの振り返りやメンバー間のコミュニケーションにも活用できます。",
|
||||||
"createdate": "2024/06/24 02:49:12"
|
"createdate": "2023/07/15 10:15:03"
|
||||||
},
|
},
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"name": "商品ID",
|
"name": "ステータス",
|
||||||
"type": "SINGLE_LINE_TEXT",
|
"type": "STATUS",
|
||||||
"code": "商品ID",
|
"code": "ステータス",
|
||||||
"label": "商品ID",
|
"label": "ステータス",
|
||||||
"noLabel": false,
|
"enabled": false
|
||||||
"required": true,
|
|
||||||
"lookup": {
|
|
||||||
"relatedApp": {
|
|
||||||
"app": "246",
|
|
||||||
"code": ""
|
|
||||||
},
|
|
||||||
"relatedKeyField": "商品ID",
|
|
||||||
"fieldMappings": [
|
|
||||||
{
|
|
||||||
"field": "商品名",
|
|
||||||
"relatedField": "商品名"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field": "単価",
|
|
||||||
"relatedField": "価格"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lookupPickerFields": [
|
|
||||||
"商品名",
|
|
||||||
"価格"
|
|
||||||
],
|
|
||||||
"filterCond": "",
|
|
||||||
"sort": "レコード番号 desc"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"name": "lookupField",
|
"name": "selectFields",
|
||||||
"placeholder": "",
|
"placeholder": "アプリ選択後、フィールドを選んでください",
|
||||||
"fieldTypes": [
|
"selectType": "multiple"
|
||||||
"lookup"
|
|
||||||
],
|
|
||||||
"hint": "更新先のルックアップフィールドを選択する"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"component": "ConditionInput",
|
"component": "AppFieldSelect",
|
||||||
"props": {
|
"props": {
|
||||||
"displayName": "更新条件",
|
"displayName": "フィールド選択(単一)",
|
||||||
"modelValue": "{\"index\":0,\"type\":\"root\",\"children\":[{\"index\":1,\"type\":\"condition\",\"parent\":\"root\",\"object\":{},\"operator\":\"=\",\"value\":\"\"}],\"parent\":null,\"logicalOperator\":\"AND\"}",
|
"modelValue": {
|
||||||
"name": "condition",
|
"app": {
|
||||||
"placeholder": "条件式を設定してください"
|
"id": "58",
|
||||||
|
"name": "日報",
|
||||||
|
"description": "",
|
||||||
|
"createdate": "2023/07/13 19:05:26"
|
||||||
|
},
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "所感、学び",
|
||||||
|
"type": "MULTI_LINE_TEXT",
|
||||||
|
"code": "文字列__複数行__0",
|
||||||
|
"label": "所感、学び",
|
||||||
|
"noLabel": false,
|
||||||
|
"required": false,
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "selectField",
|
||||||
|
"placeholder": "アプリ選択後、フィールドを選んでください",
|
||||||
|
"selectType": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "ColorPicker",
|
||||||
|
"props": {
|
||||||
|
"displayName": "色選択",
|
||||||
|
"modelValue": "#f50000",
|
||||||
|
"name": "color",
|
||||||
|
"placeholder": "カラーを選択してください"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "NumInput",
|
||||||
|
"props": {
|
||||||
|
"displayName": "数値入力フィールド",
|
||||||
|
"modelValue": 100,
|
||||||
|
"name": "num",
|
||||||
|
"max": 100,
|
||||||
|
"min": 0,
|
||||||
|
"placeholder": "数値を入力してください"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"prevNodeId": "822b5b2c-cfb7-447a-ae77-01bf3a530fae",
|
"prevNodeId": "c5cd772a-04be-418e-a811-3787f98a2285",
|
||||||
"nextNodeIds": {}
|
"nextNodeIds": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
75
sample2.json
75
sample2.json
@@ -1,24 +1,55 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"component": "AppFieldSelect",
|
"component": "InputText",
|
||||||
"props": {
|
"props": {
|
||||||
"displayName": "更新先選択",
|
"displayName": "文字入力",
|
||||||
"modelValue": {},
|
"modelValue": "",
|
||||||
"name": "lookupField",
|
"name": "str",
|
||||||
"placeholder": "",
|
"placeholder": "文字を入力してください",
|
||||||
"fieldTypes": [
|
"maxLength":"20",
|
||||||
"lookup"
|
"hint":"文字列入力<br>入力ルール指定可能。ルールの設定例:[val=>!!val||'必須入力です']",
|
||||||
],
|
"rules":"[val=>!!val||'必須入力です']"
|
||||||
"hint": "更新先のルックアップフィールドを選択する"
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "AppFieldSelect",
|
||||||
|
"props": {
|
||||||
|
"displayName": "フィールド選択(複数)",
|
||||||
|
"modelValue": {},
|
||||||
|
"name": "selectFields",
|
||||||
|
"placeholder": "アプリ選択後、フィールドを選んでください",
|
||||||
|
"selectType":"multiple"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "AppFieldSelect",
|
||||||
|
"props": {
|
||||||
|
"displayName": "フィールド選択(単一)",
|
||||||
|
"modelValue": {},
|
||||||
|
"name": "selectField",
|
||||||
|
"placeholder": "アプリ選択後、フィールドを選んでください",
|
||||||
|
"selectType":"single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "ColorPicker",
|
||||||
|
"props": {
|
||||||
|
"displayName": "色選択",
|
||||||
|
"modelValue": "",
|
||||||
|
"name": "color",
|
||||||
|
"placeholder": "カラーを選択してください"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"component": "NumInput",
|
||||||
|
"props": {
|
||||||
|
"displayName": "数値入力フィールド",
|
||||||
|
"modelValue": "",
|
||||||
|
"name": "num",
|
||||||
|
"max":100,
|
||||||
|
"min":0,
|
||||||
|
"placeholder": "数値を入力してください",
|
||||||
|
"rules":"[val=>!!val ||'数値を入力してください',val=>val<=100 && val>=1 || '1-100の範囲内の数値を入力してください']"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
|
||||||
"component": "ConditionInput",
|
|
||||||
"props": {
|
|
||||||
"displayName": "更新条件",
|
|
||||||
"modelValue": "",
|
|
||||||
"name": "condition",
|
|
||||||
"placeholder": "条件式を設定してください"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
Reference in New Issue
Block a user