Compare commits
1 Commits
feature-ap
...
devForBF
| Author | SHA1 | Date | |
|---|---|---|---|
| 15cc48cd40 |
28
backend/app/core/operation.py
Normal file
28
backend/app/core/operation.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from app.db.crud import create_operationlog
|
||||||
|
from app.db.schemas import OperationCreate
|
||||||
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
|
||||||
|
def log_operation(operation):
|
||||||
|
def decorator(func):
|
||||||
|
@wraps(func)
|
||||||
|
async def wrapper(*args,**kwargs):
|
||||||
|
db = kwargs.get('db')
|
||||||
|
request = kwargs.get('request')
|
||||||
|
result = await func(*args,**kwargs)
|
||||||
|
detial = f"Request: {request.method} {request.url}\
|
||||||
|
Request Headers: {request.headers}\
|
||||||
|
Request Body: {request.body()}\
|
||||||
|
Result: {jsonable_encoder(result,exclude_unset=True)}"
|
||||||
|
function = func.__name__
|
||||||
|
db_operation = OperationCreate(tenantid ="t",
|
||||||
|
domainurl="d",
|
||||||
|
userid=3,
|
||||||
|
operation=operation,
|
||||||
|
function=function,
|
||||||
|
detail=detial)
|
||||||
|
create_operationlog(db,db_operation)
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
@@ -364,4 +364,18 @@ def create_log(db: Session, error:schemas.ErrorCreate):
|
|||||||
|
|
||||||
def get_kintoneformat(db: Session):
|
def get_kintoneformat(db: Session):
|
||||||
formats = db.query(models.KintoneFormat).order_by(models.KintoneFormat.id).all()
|
formats = db.query(models.KintoneFormat).order_by(models.KintoneFormat.id).all()
|
||||||
return formats
|
return formats
|
||||||
|
|
||||||
|
def create_operationlog(db: Session, log: schemas.OperationCreate):
|
||||||
|
db_log = models.OperationLog(
|
||||||
|
tenantid=log.tenantid,
|
||||||
|
domainurl=log.domainurl,
|
||||||
|
userid = log.userid,
|
||||||
|
operation = log.operation,
|
||||||
|
function = log.function,
|
||||||
|
detail = log.detail
|
||||||
|
)
|
||||||
|
db.add(db_log)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_log)
|
||||||
|
return db_log
|
||||||
@@ -162,4 +162,12 @@ class Event(Base):
|
|||||||
class ErrorCreate(BaseModel):
|
class ErrorCreate(BaseModel):
|
||||||
title:str
|
title:str
|
||||||
location:str
|
location:str
|
||||||
content:str
|
content:str
|
||||||
|
|
||||||
|
class OperationCreate(BaseModel):
|
||||||
|
tenantid:str
|
||||||
|
domainurl:str
|
||||||
|
userid:int
|
||||||
|
operation:str
|
||||||
|
function:str
|
||||||
|
detail:str
|
||||||
|
|||||||
@@ -7,14 +7,18 @@
|
|||||||
<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" :class="getSelectedClass(prop.node)">{{ prop.node.label }}</div>
|
<div class="no-wrap"
|
||||||
|
:class="selectedEvent && prop.node.eventId === selectedEvent.eventId ? 'selected-node' : ''">{{prop.node.label }}
|
||||||
|
</div>
|
||||||
<q-space></q-space>
|
<q-space></q-space>
|
||||||
<!-- <q-icon v-if="prop.node.hasFlow" name="delete" color="negative" size="16px" class="q-mr-sm"></q-icon> -->
|
<!-- <q-icon v-if="prop.node.hasFlow" name="delete" color="negative" size="16px" class="q-mr-sm"></q-icon> -->
|
||||||
</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-start no-wrap event-node">
|
||||||
<div class="no-wrap" :class="getSelectedClass(prop.node)">{{ prop.node.label }}</div>
|
<div class="no-wrap"
|
||||||
|
: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>
|
||||||
@@ -23,7 +27,7 @@
|
|||||||
<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-start event-node" @click="onSelected(prop.node)">
|
||||||
<q-icon v-if="prop.node.eventId" name="play_circle" :color="prop.node.hasFlow ? 'green' : 'grey'" size="16px" class="q-mr-sm" />
|
<q-icon v-if="prop.node.eventId" name="play_circle" :color="prop.node.hasFlow ? 'green' : 'grey'" size="16px" class="q-mr-sm" />
|
||||||
<div class="no-wrap" :class="getSelectedClass(prop.node)">{{ prop.node.label }}</div>
|
<div class="no-wrap" :class="selectedEvent && prop.node.eventId === selectedEvent.eventId ? 'selected-node' : ''" >{{ prop.node.label}}</div>
|
||||||
<q-space></q-space>
|
<q-space></q-space>
|
||||||
<q-icon name="delete_forever" color="negative" size="16px" @click="deleteEvent(prop.node)"></q-icon>
|
<q-icon name="delete_forever" color="negative" size="16px" @click="deleteEvent(prop.node)"></q-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,7 +42,7 @@
|
|||||||
import { QTree, useQuasar } from 'quasar';
|
import { QTree, useQuasar } from 'quasar';
|
||||||
import { ActionFlow, RootAction } from 'src/types/ActionTypes';
|
import { ActionFlow, RootAction } from 'src/types/ActionTypes';
|
||||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
import { useFlowEditorStore } from 'stores/flowEditor';
|
||||||
import { defineComponent, ref, watchEffect } from 'vue';
|
import { defineComponent, ref,watchEffect } from 'vue';
|
||||||
import { IKintoneEvent, IKintoneEventGroup, IKintoneEventNode, kintoneEvent } from '../../types/KintoneEvents';
|
import { IKintoneEvent, IKintoneEventGroup, IKintoneEventNode, kintoneEvent } from '../../types/KintoneEvents';
|
||||||
import FieldSelect from '../FieldSelect.vue';
|
import FieldSelect from '../FieldSelect.vue';
|
||||||
import ShowDialog from '../ShowDialog.vue';
|
import ShowDialog from '../ShowDialog.vue';
|
||||||
@@ -76,11 +80,6 @@ export default defineComponent({
|
|||||||
const isFieldChange = (node: IKintoneEventNode) => {
|
const isFieldChange = (node: IKintoneEventNode) => {
|
||||||
return node.header == 'EVENT' && node.eventId.indexOf(".change.") > -1;
|
return node.header == 'EVENT' && node.eventId.indexOf(".change.") > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSelectedClass = (node: IKintoneEventNode) => {
|
|
||||||
return store.selectedEvent && node.eventId === store.selectedEvent.eventId ? 'selected-node' : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
//フィールド値変更イベント追加
|
//フィールド値変更イベント追加
|
||||||
const closeDg = (val: string) => {
|
const closeDg = (val: string) => {
|
||||||
if (val == 'OK') {
|
if (val == 'OK') {
|
||||||
@@ -133,7 +132,7 @@ export default defineComponent({
|
|||||||
const screen = store.eventTree.findEventById(node.parentId);
|
const screen = store.eventTree.findEventById(node.parentId);
|
||||||
|
|
||||||
let flow = store.findFlowByEventId(node.eventId);
|
let flow = store.findFlowByEventId(node.eventId);
|
||||||
let screenName = screen !== null ? screen.label : '';
|
let screenName = screen !== null ? screen.label : "";
|
||||||
let nodeLabel = node.label;
|
let nodeLabel = node.label;
|
||||||
// if(isFieldChange(node)){
|
// if(isFieldChange(node)){
|
||||||
// screenName=nodeLabel;
|
// screenName=nodeLabel;
|
||||||
@@ -160,7 +159,6 @@ export default defineComponent({
|
|||||||
tree,
|
tree,
|
||||||
showDialog,
|
showDialog,
|
||||||
isFieldChange,
|
isFieldChange,
|
||||||
getSelectedClass,
|
|
||||||
onSelected,
|
onSelected,
|
||||||
selectedEvent,
|
selectedEvent,
|
||||||
addChangeEvent,
|
addChangeEvent,
|
||||||
|
|||||||
@@ -47,18 +47,11 @@ const essentialLinks: EssentialLinkProps[] = [
|
|||||||
link: '/',
|
link: '/',
|
||||||
target: '_self'
|
target: '_self'
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// title: 'フローエディター',
|
|
||||||
// caption: 'イベントを設定する',
|
|
||||||
// icon: 'account_tree',
|
|
||||||
// link: '/#/FlowChart',
|
|
||||||
// target: '_self'
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: 'アプリ管理',
|
title: 'フローエディター',
|
||||||
caption: 'アプリを管理する',
|
caption: 'イベントを設定する',
|
||||||
icon: 'widgets',
|
icon: 'account_tree',
|
||||||
link: '/#/app',
|
link: '/#/FlowChart',
|
||||||
target: '_self'
|
target: '_self'
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="q-pa-md">
|
|
||||||
<div class="q-gutter-sm row items-start">
|
|
||||||
<q-breadcrumbs>
|
|
||||||
<q-breadcrumbs-el icon="widgets" label="アプリ管理" />
|
|
||||||
</q-breadcrumbs>
|
|
||||||
</div>
|
|
||||||
<q-table title="Treats" :rows="rows" :columns="columns" row-key="id" :filter="filter" :loading="loading" :pagination="pagination">
|
|
||||||
|
|
||||||
<template v-slot:top>
|
|
||||||
<q-btn disabled color="primary" :disable="loading" label="新規" @click="addRow" />
|
|
||||||
<q-space />
|
|
||||||
<q-input borderless dense filled debounce="300" v-model="filter" placeholder="検索">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:body-cell-actions="p">
|
|
||||||
<q-td :props="p">
|
|
||||||
<q-btn-group flat>
|
|
||||||
<q-btn flat color="primary" padding="xs" size="1em" icon="edit_note" @click="editFlow(p.row)" />
|
|
||||||
<q-btn disabled flat color="primary" padding="xs" size="1em" icon="history" @click="showHistory(p.row)" />
|
|
||||||
<q-btn disabled flat color="negative" padding="xs" size="1em" icon="delete_outline" @click="removeRow(p.row)" />
|
|
||||||
</q-btn-group>
|
|
||||||
</q-td>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</q-table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted, reactive } from 'vue';
|
|
||||||
import { api } from 'boot/axios';
|
|
||||||
import { useAuthStore } from 'stores/useAuthStore';
|
|
||||||
import { useFlowEditorStore } from 'stores/flowEditor';
|
|
||||||
import { router } from 'src/router';
|
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ name: 'id', label: 'アプリID', field: 'id', align: 'left', sortable: true },
|
|
||||||
{ name: 'name', label: 'アプリ名', field: 'name', align: 'left', sortable: true },
|
|
||||||
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true },
|
|
||||||
{ name: 'user', label: 'ログイン名', field: 'user', align: 'left', },
|
|
||||||
{ name: 'version', label: 'バージョン', field: 'version', align: 'left', },
|
|
||||||
{ name: 'actions', label: '操作', field: 'actions' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
|
|
||||||
const loading = ref(false);
|
|
||||||
const filter = ref('');
|
|
||||||
const rows = ref([]);
|
|
||||||
const store = useFlowEditorStore();
|
|
||||||
|
|
||||||
const tenantid = ref(authStore.currentDomain.id);
|
|
||||||
|
|
||||||
const getApps = async () => {
|
|
||||||
loading.value = true;
|
|
||||||
const result = await api.get('api/apps');
|
|
||||||
rows.value = result.data.map((item) => {
|
|
||||||
return { id: item.appid, name: item.appname, url: item.domainurl, user: item.user.email, version: item.version }
|
|
||||||
});
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await getApps();
|
|
||||||
})
|
|
||||||
|
|
||||||
const addRow = () => {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeRow = (row) => {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const showHistory = (row) => {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const editFlow = (row) => {
|
|
||||||
store.setApp({
|
|
||||||
appId: row.id,
|
|
||||||
name: row.name
|
|
||||||
});
|
|
||||||
router.push('/FlowChart');
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -290,8 +290,20 @@ const onSaveAllFlow= async ()=>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
await store.loadFlow();
|
|
||||||
drawerLeft.value = true;
|
drawerLeft.value = true;
|
||||||
|
if (store.appInfo === undefined) return;
|
||||||
|
const flowCtrl = new FlowCtrl();
|
||||||
|
const actionFlows = await flowCtrl.getFlows(store.appInfo?.appId);
|
||||||
|
if (actionFlows && actionFlows.length > 0) {
|
||||||
|
store.setFlows(actionFlows);
|
||||||
|
}
|
||||||
|
if (actionFlows && actionFlows.length == 1) {
|
||||||
|
store.selectFlow(actionFlows[0]);
|
||||||
|
}
|
||||||
|
const root = actionFlows[0].getRoot();
|
||||||
|
if (root) {
|
||||||
|
store.setActiveNode(root);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClearFilter=()=>{
|
const onClearFilter=()=>{
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const routes: RouteRecordRaw[] = [
|
|||||||
{ path: 'domain', component: () => import('pages/TenantDomain.vue') },
|
{ path: 'domain', component: () => import('pages/TenantDomain.vue') },
|
||||||
{ path: 'userdomain', component: () => import('pages/UserDomain.vue')},
|
{ path: 'userdomain', component: () => import('pages/UserDomain.vue')},
|
||||||
{ path: 'user', component: () => import('pages/UserManagement.vue')},
|
{ path: 'user', component: () => import('pages/UserManagement.vue')},
|
||||||
{ path: 'app', component: () => import('pages/AppManagement.vue')},
|
|
||||||
{ path: 'condition', component: () => import('pages/conditionPage.vue') }
|
{ path: 'condition', component: () => import('pages/conditionPage.vue') }
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -64,9 +64,7 @@ export const useFlowEditorStore = defineStore('flowEditor', {
|
|||||||
this.selectedFlow = flow;
|
this.selectedFlow = flow;
|
||||||
if(flow!==undefined){
|
if(flow!==undefined){
|
||||||
const eventId = flow.getRoot()?.name;
|
const eventId = flow.getRoot()?.name;
|
||||||
this.selectedEvent = this.eventTree.findEventById(eventId) as IKintoneEvent;
|
this.selectedEvent=this.eventTree.findEventById(eventId) as IKintoneEvent;
|
||||||
} else {
|
|
||||||
this.selectedEvent = undefined;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setActiveNode(node: IActionNode) {
|
setActiveNode(node: IActionNode) {
|
||||||
@@ -88,8 +86,8 @@ export const useFlowEditorStore = defineStore('flowEditor', {
|
|||||||
//eventTreeにバンドする
|
//eventTreeにバンドする
|
||||||
this.eventTree.bindFlows(actionFlows);
|
this.eventTree.bindFlows(actionFlows);
|
||||||
if (actionFlows === undefined || actionFlows.length === 0) {
|
if (actionFlows === undefined || actionFlows.length === 0) {
|
||||||
this.setFlows([]);
|
this.flows = [];
|
||||||
this.selectFlow(undefined);
|
this.selectedFlow = undefined;
|
||||||
this.expandedScreen =[];
|
this.expandedScreen =[];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -97,11 +95,6 @@ export const useFlowEditorStore = defineStore('flowEditor', {
|
|||||||
if (actionFlows && actionFlows.length > 0) {
|
if (actionFlows && actionFlows.length > 0) {
|
||||||
this.selectFlow(actionFlows[0]);
|
this.selectFlow(actionFlows[0]);
|
||||||
}
|
}
|
||||||
const root = actionFlows[0].getRoot();
|
|
||||||
if (root) {
|
|
||||||
this.setActiveNode(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandEventIds = actionFlows.map((flow) => flow.getRoot()?.name);
|
const expandEventIds = actionFlows.map((flow) => flow.getRoot()?.name);
|
||||||
const expandScreens:string[]=[];
|
const expandScreens:string[]=[];
|
||||||
expandEventIds.forEach((eventid)=>{
|
expandEventIds.forEach((eventid)=>{
|
||||||
|
|||||||
Reference in New Issue
Block a user