diff --git a/document/サイトマップ.drawio b/document/サイトマップ.drawio index d85255a..bb09a2a 100644 --- a/document/サイトマップ.drawio +++ b/document/サイトマップ.drawio @@ -1,144 +1,144 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/frontend/quasar.config.js b/frontend/quasar.config.js index 7cd68bf..b395d57 100644 --- a/frontend/quasar.config.js +++ b/frontend/quasar.config.js @@ -36,7 +36,8 @@ module.exports = configure(function (/* ctx */) { // --> boot files are part of "main.js" // https://v2.quasar.dev/quasar-cli-vite/boot-files boot: [ - 'axios' + 'axios', + 'error-handler' ], // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css diff --git a/frontend/src/boot/axios.ts b/frontend/src/boot/axios.ts index 4577d13..b12ede8 100644 --- a/frontend/src/boot/axios.ts +++ b/frontend/src/boot/axios.ts @@ -20,22 +20,20 @@ const token=localStorage.getItem('token')||''; if(token!==''){ api.defaults.headers["Authorization"]='Bearer ' + token; } +//axios例外キャプチャー api.interceptors.response.use( (response)=>response, (error)=>{ - const orgReq=error.config; - if(error.response && error.response.status===401){ - console.error("401エラー"); + if (error.response && error.response.status === 401) { + // 認証エラーの場合再ログインする + console.error('(; ゚Д゚)/認証エラー(401):', error); localStorage.removeItem('token'); router.replace({ path:"/login", query:{redirect:router.currentRoute.value.fullPath} }); - // router.push({ - // path:"/login", - // query:{redirect:router.currentRoute.value.fullPath} - // }); } + return Promise.reject(error); } ) export default boot(({ app }) => { diff --git a/frontend/src/boot/error-handler.ts b/frontend/src/boot/error-handler.ts new file mode 100644 index 0000000..57253da --- /dev/null +++ b/frontend/src/boot/error-handler.ts @@ -0,0 +1,20 @@ +// src/boot/error-handler.ts +import { boot } from 'quasar/wrappers'; +import { Router } from 'vue-router'; +import { App } from 'vue'; + +export default boot(({ app, router }: { app: App; router: Router }) => { + app.config.errorHandler = (err: any, instance: any, info: string) => { + if (err.response && err.response.status === 401) { + // 認証エラーの場合再ログインする + console.error('(; ゚Д゚)/認証エラー(401):', err, info); + localStorage.removeItem('token'); + router.replace({ + path:"/login", + query:{redirect:router.currentRoute.value.fullPath} + }); + } else { + console.error('(; ゚Д゚)例外:', err, info); + } + }; +}); diff --git a/frontend/src/components/ConditionEditor/ConditionObject.vue b/frontend/src/components/ConditionEditor/ConditionObject.vue index ddd1c4b..3194978 100644 --- a/frontend/src/components/ConditionEditor/ConditionObject.vue +++ b/frontend/src/components/ConditionEditor/ConditionObject.vue @@ -1,30 +1,34 @@ diff --git a/frontend/src/components/VariableList.vue b/frontend/src/components/VariableList.vue new file mode 100644 index 0000000..a8d9c3a --- /dev/null +++ b/frontend/src/components/VariableList.vue @@ -0,0 +1,44 @@ + + diff --git a/frontend/src/components/right/PropertyPanel.vue b/frontend/src/components/right/PropertyPanel.vue index 4779271..82d605e 100644 --- a/frontend/src/components/right/PropertyPanel.vue +++ b/frontend/src/components/right/PropertyPanel.vue @@ -13,7 +13,7 @@ > -
{{ actionNode.subTitle }}:設定
+
{{ actionNode?.subTitle }}:設定
@@ -51,10 +51,10 @@ import { IActionNode } from 'src/types/ActionTypes'; ], setup(props,{emit}) { const showPanel =ref(props.drawerRight); - const actionProps =ref(props.actionNode.actionProps); + const actionProps =ref(props.actionNode?.actionProps); watchEffect(() => { showPanel.value = props.drawerRight; - actionProps.value= props.actionNode.actionProps; + actionProps.value= props.actionNode?.actionProps; }); const cancel = async() =>{ diff --git a/frontend/src/pages/FlowChart.vue b/frontend/src/pages/FlowChart.vue index 0087db9..f7a5ce8 100644 --- a/frontend/src/pages/FlowChart.vue +++ b/frontend/src/pages/FlowChart.vue @@ -22,12 +22,12 @@
-
- + @@ -57,12 +57,7 @@ const drawerLeft = ref(false); const $q = useQuasar(); const store = useFlowEditorStore(); const authStore = useAuthStore(); -// ref関数を使ってtemplateとバインド -const state = reactive({ - activeNode: { - id: "" - }, -}) + const appDg = ref(); const prevNodeIfo = ref({ prevNode: {} as IActionNode, @@ -88,21 +83,21 @@ const addNode = (node: IActionNode, inputPoint: string) => { const onNodeSelected = (node: IActionNode) => { //右パネルが開いている場合、自動閉じる - if (drawerRight.value && state.activeNode.id !== node.id) { + if (drawerRight.value && store.activeNode?.id !== node.id) { drawerRight.value = false; } - state.activeNode = node; + store.setActiveNode(node); } const onNodeEdit = (node: IActionNode) => { - state.activeNode = node; + store.setActiveNode(node); drawerRight.value = true; } const onDeleteNode = (node: IActionNode) => { if (!store.currentFlow) return; //右パネルが開いている場合、自動閉じる - if (drawerRight.value && state.activeNode.id === node.id) { + if (drawerRight.value && store.activeNode?.id === node.id) { drawerRight.value = false; } store.currentFlow?.removeNode(node); @@ -219,7 +214,7 @@ const fetchData = async () => { } const root = actionFlows[0].getRoot(); if (root) { - state.activeNode = root; + store.setActiveNode(root); } } diff --git a/frontend/src/stores/flowEditor.ts b/frontend/src/stores/flowEditor.ts index 95e9736..eefca34 100644 --- a/frontend/src/stores/flowEditor.ts +++ b/frontend/src/stores/flowEditor.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia'; -import { AppInfo ,IActionFlow} from 'src/types/ActionTypes'; +import { AppInfo ,IActionFlow, IActionNode} from 'src/types/ActionTypes'; import { kintoneEvents,IKintoneEvent,KintoneEventManager } from 'src/types/KintoneEvents'; import {FlowCtrl } from '../control/flowctrl'; @@ -8,6 +8,7 @@ export interface FlowEditorState{ appInfo?:AppInfo; flows?:IActionFlow[]; selectedFlow?:IActionFlow|undefined; + activeNode:IActionNode|undefined; eventTree:KintoneEventManager; selectedEvent:IKintoneEvent|undefined; expandedScreen:any[]; @@ -19,6 +20,7 @@ export const useFlowEditorStore = defineStore("flowEditor",{ appInfo:undefined, flows:[], selectedFlow:undefined, + activeNode:undefined, eventTree:kintoneEvents, selectedEvent:undefined, expandedScreen:[] @@ -53,6 +55,9 @@ export const useFlowEditorStore = defineStore("flowEditor",{ selectFlow(flow:IActionFlow){ this.selectedFlow=flow; }, + setActiveNode(node:IActionNode){ + this.activeNode=node; + }, setApp(app:AppInfo){ this.appInfo=app; }, diff --git a/frontend/src/types/ActionTypes.ts b/frontend/src/types/ActionTypes.ts index c98ed30..aeafe35 100644 --- a/frontend/src/types/ActionTypes.ts +++ b/frontend/src/types/ActionTypes.ts @@ -16,11 +16,9 @@ export interface AppInfo { } /** -* アクションのプロパティ定義 + * 属性項目情報 */ -export interface IActionProperty { - component: string; - props: { +export interface IProp{ //プロパティ名 name: string; //プロパティ表示名 @@ -30,7 +28,22 @@ export interface IActionProperty { hint:string; //プロパティ設定値 modelValue: any; - }; +} + +/** +* アクションのプロパティ定義 + */ +export interface IActionProperty { + component: string; + props: IProp; +} +/** + * 変数オブジェクト + */ +export interface IActionVariable{ + actionName:string; + displayName:string; + name:string; } /** * アクションタイプ定義 @@ -40,6 +53,7 @@ export interface IActionNode { //アクション名 name: string; title: string; + varName:IProp|undefined; subTitle: string; inputPoint: string; //出力ポイント(条件分岐以外未使用) @@ -62,6 +76,7 @@ export interface IActionFlow { addNode(newNode: IActionNode, prevNode?: IActionNode, inputPoint?: string): IActionNode; removeNode(targetNode: IActionNode): boolean; removeAllNext(targetNodeId: string): void; + getVarNames(currentNode:IActionNode):IActionVariable[]; findNodeById(id: string): IActionNode | undefined; toJSON(): any; getRoot(): IActionNode | undefined; @@ -73,16 +88,7 @@ export interface IActionFlow { */ class ActionProperty implements IActionProperty { component: string; - props: { - // プロパティ名 - name: string; - // プロパティ表示名 - displayName: string; - placeholder: string; - hint:string; - // プロパティ設定値 - modelValue: any; - }; + props: IProp; static defaultProperty(): IActionProperty { return new ActionProperty('InputText', 'displayName', '表示名', '表示を入力してください', '',''); @@ -121,6 +127,13 @@ export class ActionNode implements IActionNode { get subTitle(): string { return this.name; }; + + //変数名 + get varName():IProp|undefined{ + const prop = this.actionProps.find((prop) => prop.props.name === "verName"); + return prop?.props; + } + inputPoint: string; //出力ポイント(条件分岐以外未使用) outputPoints: Array; @@ -168,6 +181,7 @@ export class RootAction implements IActionNode { title: string; subTitle: string; inputPoint: string; + varName: IProp | undefined=undefined; //出力ポイント(条件分岐以外未使用) outputPoints: Array; isRoot: boolean; @@ -225,7 +239,7 @@ export class ActionFlow implements IActionFlow { newNode.inputPoint = inputPoint; } if (prevNode !== undefined) { - this.connectNodes(prevNode, newNode, inputPoint || ''); + this.resetNodeRelation(prevNode, newNode, inputPoint || ''); } else { prevNode = this.actionNodes[this.actionNodes.length - 1]; this.connectNodes(prevNode, newNode, inputPoint || ''); @@ -319,8 +333,7 @@ export class ActionFlow implements IActionFlow { if (!nextNodeId) return; const nextNode = this.findNodeById(nextNodeId); if (!nextNode) return; - nextNode.prevNodeId = prevNode.id; - prevNode.nextNodeIds.set(targetNode.inputPoint || '', nextNodeId); + this.connectNodes(prevNode,nextNode,targetNode.inputPoint || ''); return; } //二つ以上の場合 @@ -354,15 +367,16 @@ export class ActionFlow implements IActionFlow { /** * - * @param prevNode ノードの接続をリセットする + * @param prevNode ノード挿入時の接続をリセットする * @param newNode * @param inputPoint */ resetNodeRelation(prevNode: IActionNode, newNode: IActionNode, inputPoint?: string) { - // + //元の次のノードを取得 + const originalNextNodeId = prevNode.nextNodeIds.get(inputPoint || ''); prevNode.nextNodeIds.set(inputPoint || '', newNode.id); newNode.prevNodeId = prevNode.id; - const originalNextNodeId = prevNode.nextNodeIds.get(inputPoint || ''); + newNode.inputPoint=inputPoint||''; this.setNewNodeNextId(newNode, originalNextNodeId, inputPoint); } @@ -374,15 +388,18 @@ export class ActionFlow implements IActionFlow { */ private setNewNodeNextId(newNode: IActionNode, originalNextNodeId: string | undefined, inputPoint?: string) { // 元の接続ノードが存在する - if (originalNextNodeId) { - // 新しいノードの outputPoints に該当 inputPointが存在するか場合をチェックする - if (newNode.outputPoints.includes(inputPoint || '')) { - newNode.nextNodeIds.set(inputPoint || '', originalNextNodeId); - } else { - // inputPointが存在しない場合、outputPointのポイントの任意ポートを選択する - const alternativeOutputPoint = newNode.outputPoints.length > 0 ? newNode.outputPoints[0] : ''; - newNode.nextNodeIds.set(alternativeOutputPoint, originalNextNodeId); - } + if (!originalNextNodeId) {return;} + const originNextNode = this.findNodeById(originalNextNodeId); + if (!originNextNode) {return;} + // 新しいノードの outputPoints に該当 inputPointが存在するか場合をチェックする + if (newNode.outputPoints.includes(inputPoint || '')) { + newNode.nextNodeIds.set(inputPoint || '', originalNextNodeId); + originNextNode.prevNodeId=newNode.id; + } else { + // inputPointが存在しない場合、outputPointのポイントの任意ポートを選択する + const alternativeOutputPoint = newNode.outputPoints.length > 0 ? newNode.outputPoints[0] : ''; + newNode.nextNodeIds.set(alternativeOutputPoint, originalNextNodeId); + originNextNode.prevNodeId=newNode.id; } } @@ -393,6 +410,36 @@ export class ActionFlow implements IActionFlow { return this.actionNodes.find((node) => node.id === id); } + getVarNames(currentNode:IActionNode):IActionVariable[]{ + let varNames:IActionVariable[]=[]; + if(currentNode.prevNodeId!==undefined){ + const prevNode=this.findNodeById(currentNode.prevNodeId); + if(prevNode!==undefined){ + varNames = this.getPrevVarNames(prevNode); + } + } + return varNames; + } + + getPrevVarNames(prevNode:IActionNode):IActionVariable[]{ + let varNames:IActionVariable[]=[]; + if(prevNode.varName!==undefined){ + varNames.unshift({ + actionName:prevNode.name, + displayName:prevNode.varName.displayName, + name:prevNode.varName.modelValue + }); + } + if(prevNode.prevNodeId!==undefined){ + const prevPrevNode=this.findNodeById(prevNode.prevNodeId); + if(prevPrevNode!==undefined){ + const prevVars = this.getPrevVarNames(prevPrevNode); + varNames=[...prevVars,...varNames]; + } + } + return varNames; + } + toJSON() { return { id: this.id, diff --git a/plugin/kintone-addins/src/actions/auto-numbering.ts b/plugin/kintone-addins/src/actions/auto-numbering.ts index 559f84b..63ae2ef 100644 --- a/plugin/kintone-addins/src/actions/auto-numbering.ts +++ b/plugin/kintone-addins/src/actions/auto-numbering.ts @@ -35,7 +35,13 @@ export class AutoNumbering implements IAction{ globalThis.window.$format=this.format; this.register(); } - +/** + * アクションの処理を実装する + * @param actionNode アクションノード + * @param event Kintoneのイベント + * @param context コンテキスト(レコード、変数情報を持っている) + * @returns + */ async process(actionNode:IActionNode,event:any,context:IContext):Promise { let result={ canNext:false,