Merge branch 'dev3' into dev2

This commit is contained in:
2025-03-04 16:53:41 +09:00
47 changed files with 314 additions and 277 deletions

View File

@@ -46,6 +46,9 @@ async def login(request: Request,db:Session= Depends(get_db) ,form_data: OAuth2P
data={"sub": user.id,"roles":roles,"permissions": permissions,"tenant":user.tenantid,},
expires_delta=access_token_expires,
)
request.state.user = user.id
return JSONResponse(
status_code=200,
content={"access_token": access_token, "token_type": "bearer","user_name":user.first_name + " " + user.last_name}

View File

@@ -9,7 +9,7 @@ from app.core import security
from app.db.cruddb import userService
from app.core.dbmanager import get_db
async def get_current_user(security_scopes: SecurityScopes,
async def get_current_user(request: Request,security_scopes: SecurityScopes,
db=Depends(get_db), token: str = Depends(security.oauth2_scheme)
):
credentials_exception = HTTPException(
@@ -42,6 +42,7 @@ async def get_current_user(security_scopes: SecurityScopes,
user = userService.get_user(db, token_data.id)
if user is None:
raise credentials_exception
request.state.user = user.id
return user
async def get_current_active_user(

View File

@@ -1,8 +1,8 @@
import time
from typing import Any
from sqlalchemy.orm import Session
from app.db.cruddb import domainService
from app.db.cruddb import tenantService
from app.db.cruddb import domainService,tenantService
from app.db.session import Database
class MemoryCache:
def __init__(self, max_cache_size: int = 100, ttl: int = 60):
@@ -65,7 +65,8 @@ class tenantCache:
if not self.memoryCache.get(f"TENANT_{tenantid}"):
tenant = tenantService.get_tenant(db,tenantid)
if tenant:
self.memoryCache.set(f"TENANT_{tenantid}",tenant.db)
database = Database(tenant.db)
self.memoryCache.set(f"TENANT_{tenantid}",database)
return self.memoryCache.get(f"TENANT_{tenantid}")
tenantCacheService =tenantCache()

View File

@@ -1,13 +1,15 @@
from fastapi import Depends
from app.db.session import get_tenant_db,get_user_db
from fastapi import Depends,Request
from app.db.session import get_tenant_db
from app.core import tenantCacheService
from app.db.session import tenantdb
def get_db(tenant:str = "1",tenantdb = Depends(get_tenant_db)):
db_url = tenantCacheService.get_tenant_db(tenantdb,tenant)
db = get_user_db(db_url)
def get_db(request: Request,tenant:str = "1",tenantdb = Depends(get_tenant_db)):
database = tenantCacheService.get_tenant_db(tenantdb,tenant)
try:
db = database.get_db()
request.state.tenant = tenant
request.state.db = db
yield db
finally:
db.close()

View File

@@ -1,52 +1,75 @@
from fastapi import Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from sqlalchemy.orm import Session
from app.db.models import OperationLog,User
from app.core.apiexception import APIException
from app.core.dbmanager import get_log_db
from app.db.crud import create_log
import json
from functools import wraps
from fastapi import Request
def log_operation(func):
"""自定义装饰器用于记录操作日志"""
@wraps(func)
async def wrapper(*args, **kwargs):
if 'request' in kwargs and isinstance(kwargs['request'], Request):
request: Request = kwargs['request']
method = request.method
url = str(request.url)
client_ip = request.client.host
headers = dict(request.headers)
user_agent = headers.get("user-agent", "")
if 'db' in kwargs and isinstance(kwargs['db'], Session):
db = kwargs['db']
if 'user' in kwargs and isinstance(kwargs['user'], User):
user = kwargs['user']
user_id = user.id
tenant_id = user.tenantid
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if request.method in ("POST", "PUT", "PATCH","DELETE"):
try:
request.state.body = await request.json()
except json.JSONDecodeError:
request.state.body = await request.body()
else:
user_id = None
tenant_id = None
request.state.body = None
try:
response = await func(*args, **kwargs)
if db:
db_operation = OperationLog(tenantid =tenant_id,
clientip =client_ip,
useragent =user_agent,
userid = user_id,
operation = method,
function = url,
response = await call_next(request)
state = request.state
except Exception as e:
await self.log_error(request, e)
response = JSONResponse(
content={"detail": "Internal Server Error"},
status_code=500
)
if hasattr(request.state, "user") and hasattr(request.state, "tenant"):
await self.log_request(request, response,state)
return response
async def log_request(self, request: Request, response,state):
try:
headers = dict(request.headers)
route = request.scope.get("route")
if route:
path_template = route.path
else:
path_template = request.url.path
db_operation = OperationLog(tenantid =request.state.tenant,
clientip = request.client.host if request.client else None,
useragent =headers.get("user-agent", ""),
userid = request.state.user,
operation = request.method,
function = path_template,
parameters = str({"path": request.path_params,"query": dict(request.query_params),"body": request.state.body}),
response = f"status_code:{response.status_code }" )
db = request.state.db
if db:
await self.write_log_to_db(db_operation,db)
except Exception as e:
print(f"Logging failed: {str(e)}")
async def log_error(self, request: Request, e: Exception):
exc = APIException('operation:dispatch',request.url._url,f"Error occurred while writting operation log:",e)
db = get_log_db()
try:
create_log(db,exc.error)
finally:
db.close()
async def write_log_to_db(self, db_operation,db):
db.add(db_operation)
db.commit()
except Exception as e:
raise e
return response
return wrapper

View File

@@ -1,14 +1,14 @@
from sqlalchemy import Boolean, Column, Integer, String, DateTime,ForeignKey,Table
from sqlalchemy.orm import Mapped,relationship,as_declarative,mapped_column
from datetime import datetime
from datetime import datetime,timezone
from app.db import Base
from app.core.security import chacha20Decrypt
@as_declarative()
class Base:
id = Column(Integer, primary_key=True, index=True)
create_time = Column(DateTime, default=datetime.now)
update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now)
create_time = Column(DateTime, default=datetime.now(timezone.utc))
update_time = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
userrole = Table(
@@ -234,6 +234,7 @@ class OperationLog(Base):
userid = mapped_column(Integer,ForeignKey("user.id"))
operation = mapped_column(String(200))
function = mapped_column(String(200))
parameters = mapped_column(String(200))
response = mapped_column(String(200))
user = relationship('User')

View File

@@ -20,7 +20,7 @@ from app.db.crud import create_log
from fastapi.responses import JSONResponse
import asyncio
from contextlib import asynccontextmanager
from app.core.operation import LoggingMiddleware
#Base.metadata.create_all(bind=engine)
@@ -45,6 +45,8 @@ app.add_middleware(
allow_headers=["*"],
)
app.add_middleware(LoggingMiddleware)
add_pagination(app)
# @app.middleware("http")

View File

@@ -105,7 +105,7 @@ module.exports = configure(function (/* ctx */) {
config: {},
// iconSet: 'material-icons', // Quasar icon set
// lang: 'en-US', // Quasar language pack
lang: 'ja', // Quasar language pack
// For special cases outside of where the auto-import strategy can have an impact
// (like functional components as one of the examples),

View File

@@ -4,14 +4,14 @@ import { Router } from 'vue-router';
import { App } from 'vue';
export default boot(({ app, router }: { app: App<Element>; router: Router }) => {
document.documentElement.lang="ja-JP";
document.documentElement.lang='ja-JP';
app.config.errorHandler = (err: any, instance: any, info: string) => {
if (err.response && err.response.status === 401) {
// 認証エラーの場合再ログインする
console.error('(; ゚Д゚)/認証エラー(401)', err, info);
localStorage.removeItem('token');
router.replace({
path:"/login",
path:'/login',
query:{redirect:router.currentRoute.value.fullPath}
});
} else {

View File

@@ -52,7 +52,7 @@ export default {
filter:String
},
emits:[
"clearFilter"
'clearFilter'
],
setup(props,{emit}) {
const isLoaded=ref(false);

View File

@@ -46,9 +46,9 @@ export default defineComponent({
const { app } = toRefs(props);
const authStore = useAuthStore();
const appinfo = ref<AppInfo>({
appId: "",
name: "",
description: ""
appId: '',
name: '',
description: ''
});
const link= ref(`${authStore.currentDomain.kintoneUrl}/k/${app.value}`);
const getAppInfo = async (appId:string|undefined) => {
@@ -56,7 +56,7 @@ export default defineComponent({
return;
}
let result : any ={appId:"",name:""};
let result : any ={appId:'',name:''};
let retry =0;
while(retry<=3 && result && result.appId!==appId){
await new Promise(resolve => setTimeout(resolve, 1000));

View File

@@ -175,7 +175,7 @@ export default defineComponent({
if (flowStore.appInfo?.appId === selected.id) {
$q.notify({
type: 'negative',
caption: "エラー",
caption: 'エラー',
message: 'データソースを現在のアプリにすることはできません。'
});
} else if (selected.id !== data.value.sourceApp.id) {
@@ -208,7 +208,7 @@ export default defineComponent({
if (isDuplicate) {
$q.notify({
type: 'negative',
caption: "エラー",
caption: 'エラー',
message: '重複したフィールドは選択できません'
});
} else {

View File

@@ -42,9 +42,9 @@ import { useQuasar } from 'quasar';
}
},
emits:[
"closed",
"update:conditionTree",
"update:show"
'closed',
'update:conditionTree',
'update:show'
],
setup(props,context) {
const appDg = ref();
@@ -58,11 +58,11 @@ import { useQuasar } from 'quasar';
// message: `条件式を設定してください。`
// });
// }
context.emit("update:conditionTree",tree.value);
context.emit('update:conditionTree',tree.value);
}
showflg.value=false;
context.emit("update:show",false);
context.emit("closed",val);
context.emit('update:show',false);
context.emit('closed',val);
};
const showflg =ref(props.show);
//条件式をコピーする

View File

@@ -217,7 +217,7 @@ export default defineComponent( {
const canMerge =(node:INode)=>{
const checkedIndexs:number[] = ticked.value;
const findNode = checkedIndexs.find(index=>node.index===index);
console.log("findNode=>",findNode!==undefined,findNode);
console.log('findNode=>',findNode!==undefined,findNode);
return findNode!==undefined;
}
//グループ化解散

View File

@@ -12,15 +12,30 @@
field-name="files"
></q-uploader>
</div>
</template>
<script setup lang="ts">
import { createUploaderComponent, useQuasar } from 'quasar';
import { useQuasar } from 'quasar';
import { useAuthStore } from 'src/stores/useAuthStore';
import { ref } from 'vue';
const $q = useQuasar();
const authStore = useAuthStore();
const emit = defineEmits(['uploaded']);
interface Props {
title?: string;
uploadUrl?: string;
}
const headers = ref([
{ name: 'Authorization', value: 'Bearer ' + authStore.token },
]);
const props = withDefaults(defineProps<Props>(), {
title: '設計書から導入する(Excel)',
uploadUrl: `${process.env.KAB_BACKEND_URL}api/v1/createappfromexcel?format=1`,
});
/**
* ファイルアップロードを拒否する時の処理
* @param rejectedEntries
@@ -30,22 +45,22 @@ import { ref } from 'vue';
// https://quasar.dev/quasar-plugins/notify#Installation
$q.notify({
type: 'negative',
message: `Excelファイルを選択してください。`
})
message: 'Excelファイルを選択してください。',
});
}
/**
* ファイルアップロード成功時の処理
*/
function onUploadFinished({ xhr }: { xhr: XMLHttpRequest }) {
let msg="ファイルのアップロードが完了しました。";
let msg = 'ファイルのアップロードが完了しました。';
if (xhr && xhr.response) {
msg = `${msg} (${xhr.responseText})`;
}
$q.notify({
type: 'positive',
caption:"通知",
message: msg
caption: '通知',
message: msg,
});
setTimeout(() => {
emit('uploaded', xhr.responseText);
@@ -69,30 +84,22 @@ import { ref } from 'vue';
*
* @param info ファイルアップロード失敗時の処理
*/
function onFailed({files,xhr}:{files: readonly any[],xhr:XMLHttpRequest}){
let msg ="ファイルアップロードが失敗しました。";
function onFailed({
files,
xhr,
}: {
files: readonly any[];
xhr: XMLHttpRequest;
}) {
let msg = 'ファイルアップロードが失敗しました。';
if (xhr && xhr.status) {
const detail = getResponseError(xhr);
msg=`${msg} (${xhr.status }:${detail})`
msg = `${msg} (${xhr.status}:${detail})`;
}
$q.notify({
type:"negative",
message:msg
type: 'negative',
message: msg,
});
}
interface Props {
title: string;
uploadUrl:string;
}
const headers = ref([{name:"Authorization",value:'Bearer ' + authStore.token}]);
const props = withDefaults(defineProps<Props>(), {
title:"設計書から導入する(Excel)",
uploadUrl: `${process.env.KAB_BACKEND_URL}api/v1/createappfromexcel?format=1`
});
</script>
<style lang="scss">
</style>
<style lang="scss"></style>

View File

@@ -42,7 +42,7 @@ export default {
onMounted(() => {
loading.value = true;
api.get(`api/domains`).then(res =>{
api.get('api/domains').then(res =>{
res.data.data.forEach((data) => {
const item = {
id: data.id,

View File

@@ -73,14 +73,14 @@ export default {
if(!props.blackListLabel.find(blackListItem => blackListItem === fld.label)){
if(props.fieldTypes.length===0 || props.fieldTypes.includes(fld.type)){
rows.push({id:index, name: fld.label || fld.code, ...fld });
}else if(props.fieldTypes.includes("lookup") && ("lookup" in fld)){
}else if(props.fieldTypes.includes('lookup') && ('lookup' in fld)){
rows.push({id:index, name: fld.label || fld.code, ...fld });
}
}
} else {
if(props.fieldTypes.length===0 || props.fieldTypes.includes(fld.type)){
rows.push({id:index, name: fld.label || fld.code, ...fld });
}else if(props.fieldTypes.includes("lookup") && ("lookup" in fld)){
}else if(props.fieldTypes.includes('lookup') && ('lookup' in fld)){
rows.push({id:index, name: fld.label || fld.code, ...fld });
}
}

View File

@@ -242,7 +242,7 @@ function isManager(userId: number) {
const getUsers = async () => {
loading.value = true;
const result = await api.get(`api/v1/users`);
const result = await api.get('api/v1/users');
allUsers.value = result.data.data.map(itemToDisplay);
loading.value = false;
}

View File

@@ -34,7 +34,7 @@ interface Props {
const props = defineProps<Props>();
async function shareApi(user: IUserDisplay, domain: IDomainOwnerDisplay) {
return api.post(`api/managedomain`, {
return api.post('api/managedomain', {
userid: user.id,
domainid: domain.id,
});

View File

@@ -29,7 +29,7 @@ interface Props {
const props = defineProps<Props>();
async function shareApi(user: IUserDisplay, domain: IDomainOwnerDisplay) {
return api.post(`api/userdomain`, {
return api.post('api/userdomain', {
userid: user.id,
domainid: domain.id,
});

View File

@@ -23,7 +23,7 @@ defineExpose({
})
const getUsers = async (filter = () => true) => {
loading.value = true;
const result = await api.get(`api/v1/users`);
const result = await api.get('api/v1/users');
rows.value = result.data.data.map((item) => {
return { id: item.id, firstName: item.first_name, lastName: item.last_name, email: item.email, isSuperuser: item.is_superuser, isActive: item.is_active }
}).filter(filter);

View File

@@ -55,7 +55,7 @@ export default {
];
const fetchUsers = async () => {
const result = await api.get(`api/v1/users`);
const result = await api.get('api/v1/users');
return result.data.data.map((item: any) => {
return { id: item.id, firstName: item.first_name, lastName: item.last_name, email: item.email, isSuperuser: item.is_superuser, isActive: item.is_active, roles: item.roles.map(role => role.id) }
}).filter(user => !props.filterInitRowsFunc || props.filterInitRowsFunc(user));

View File

@@ -44,7 +44,7 @@ import { useAuthStore } from 'src/stores/useAuthStore';
export default defineComponent({
name: 'AppSelector',
emits:[
"appSelected"
'appSelected'
],
components:{
AppSelectBox,
@@ -59,7 +59,7 @@ export default defineComponent({
const closeDg=(val :any)=>{
showSelectApp.value=false;
console.log("Dialog closed->",val);
console.log('Dialog closed->',val);
if (val == 'OK') {
const data = appDg.value.selected[0];
console.log(data);

View File

@@ -74,7 +74,7 @@ export default defineComponent({
const selectedEvent = ref<IKintoneEvent | undefined>(store.selectedEvent);
const selectedChangeEvent = ref<IKintoneEventGroup | undefined>(undefined);
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) => {
@@ -117,7 +117,7 @@ export default defineComponent({
$q.notify({
type: 'positive',
caption: "通知",
caption: '通知',
message: `イベント ${node.label} 削除`
})
}

View File

@@ -92,11 +92,11 @@ export default defineComponent({
},
emits: [
'addNode',
"nodeSelected",
"nodeEdit",
"deleteNode",
"deleteAllNextNodes",
"copyFlow"
'nodeSelected',
'nodeEdit',
'deleteNode',
'deleteAllNextNodes',
'copyFlow'
],
setup(props, context) {
const store = useFlowEditorStore();
@@ -204,7 +204,7 @@ export default defineComponent({
* 変数名取得
*/
const varName =(node:IActionNode)=>{
const prop = node.actionProps.find((prop) => prop.props.name === "verName");
const prop = node.actionProps.find((prop) => prop.props.name === 'verName');
return prop?.props.modelValue.name;
};
const copyFlow=()=>{

View File

@@ -31,11 +31,11 @@
import { ref, defineComponent, computed, PropType } from 'vue';
import { IActionNode, ActionNode, ActionFlow, RootAction } from '../../types/ActionTypes';
export enum Direction {
Default = "None",
Left = "LEFT",
Right = "RIGHT",
LeftNotNext = "LEFTNOTNEXT",
RightNotNext = "RIGHTNOTNEXT",
Default = 'None',
Left = 'LEFT',
Right = 'RIGHT',
LeftNotNext = 'LEFTNOTNEXT',
RightNotNext = 'RIGHTNOTNEXT',
}
export default defineComponent({
name: 'NodeLine',

View File

@@ -55,7 +55,7 @@ export default defineComponent({
});
const connectProps=(props:IProp)=>{
const connProps:any={};
if(props && "connectProps" in props && props.connectProps!=undefined){
if(props && 'connectProps' in props && props.connectProps!=undefined){
for(let connProp of props.connectProps){
let targetProp = componentData.value.find((prop)=>prop.props.name===connProp.propName);
if(targetProp){

View File

@@ -72,11 +72,11 @@ export default defineComponent({
}
},
setup(props, { emit }) {
const color = ref(props.modelValue??"");
const isSelected = computed(()=>props.modelValue && props.modelValue!=="");
const color = ref(props.modelValue??'');
const isSelected = computed(()=>props.modelValue && props.modelValue!=='');
const customExp = props.rules === undefined ? [] : eval(props.rules);
const errmsg = props.requiredMessage?props.requiredMessage:`${props.displayName}が必須です。`;
const requiredExp = props.required?[((val:any)=>!!val || errmsg ),"anyColor"]:[];
const requiredExp = props.required?[((val:any)=>!!val || errmsg ),'anyColor']:[];
const rulesExp=[...requiredExp,...customExp];
watchEffect(()=>{
emit('update:modelValue', color.value);

View File

@@ -91,7 +91,7 @@ export default defineComponent({
},
setup(props, { emit }) {
let source = reactive(props.connectProps["source"]);
let source = reactive(props.connectProps['source']);
if(!source){
source = props.context.find(element => element.props.name === 'sources');
}

View File

@@ -206,32 +206,32 @@ export default defineComponent({
//集計処理方法
const logicalOperators = ref([
{
"operator": "",
"label": "なし"
'operator': '',
'label': 'なし'
},
{
"operator": "SUM",
"label": "合計"
'operator': 'SUM',
'label': '合計'
},
{
"operator": "AVG",
"label": "平均"
'operator': 'AVG',
'label': '平均'
},
{
"operator": "MAX",
"label": "最大値"
'operator': 'MAX',
'label': '最大値'
},
{
"operator": "MIN",
"label": "最小値"
'operator': 'MIN',
'label': '最小値'
},
{
"operator": "COUNT",
"label": "カウント"
'operator': 'COUNT',
'label': 'カウント'
},
{
"operator": "FIRST",
"label": "最初の値"
'operator': 'FIRST',
'label': '最初の値'
}
]);
const checkInput=(val:ValueType)=>{
@@ -239,13 +239,13 @@ export default defineComponent({
return false;
}
if(!val.name){
return "集計結果の変数名を入力してください";
return '集計結果の変数名を入力してください';
}
if(!val.vars || val.vars.length==0){
return "集計処理を設定してください";
return '集計処理を設定してください';
}
if(val.vars.some((x)=>!x.vName)){
return "集計結果変数名を入力してください";
return '集計結果変数名を入力してください';
}
return true;
}

View File

@@ -70,14 +70,14 @@ export default defineComponent({
const eventId =store.currentFlow?.getRoot()?.name;
if(eventId===undefined){return;}
let displayName = inputValue.value;
if(props.connectProps!==undefined && "displayName" in props.connectProps){
displayName =props.connectProps["displayName"].props.modelValue;
if(props.connectProps!==undefined && 'displayName' in props.connectProps){
displayName =props.connectProps['displayName'].props.modelValue;
}
const customButtonId=`${eventId}.customButtonClick`;
const findedEvent = store.eventTree.findEventById(customButtonId);
if(findedEvent && "events" in findedEvent){
if(findedEvent && 'events' in findedEvent){
const customEvents = findedEvent as IKintoneEventGroup;
const addEventId = customButtonId+"." + inputValue.value;
const addEventId = customButtonId+'.' + inputValue.value;
if(store.eventTree.findEventById(addEventId)){
return;
}

View File

@@ -71,7 +71,7 @@ export default defineComponent({
const rulesExp=[...requiredExp,...customExp];
watchEffect(()=>{
emit("update:modelValue",numValue.value);
emit('update:modelValue',numValue.value);
});
return {
numValue,

View File

@@ -59,7 +59,7 @@ export default defineComponent({
const properties=ref(props.nodeProps);
const connectProps=(props:IProp)=>{
const connProps:any={context:properties};
if(props && "connectProps" in props && props.connectProps!=undefined){
if(props && 'connectProps' in props && props.connectProps!=undefined){
for(let connProp of props.connectProps){
let targetProp = properties.value.find((prop)=>prop.props.name===connProp.propName);
if(targetProp){

View File

@@ -9,7 +9,7 @@ export class Auth
params.append('username', user);
params.append('password', pwd);
try{
const result = await api.post(`api/token`,params);
const result = await api.post('api/token',params);
console.info(result);
localStorage.setItem('Token', result.data.access_token);
return true;

View File

@@ -130,15 +130,15 @@ const route = useRoute()
const appDg = ref();
const prevNodeIfo = ref({
prevNode: {} as IActionNode,
inputPoint: ""
inputPoint: ''
});
// const refFlow = ref<ActionFlow|null>(null);
const showAddAction = ref(false);
const saveVersionAction = ref(false);
const versionInputRef = ref();
const drawerRight = ref(false);
const filter=ref("");
const model = ref("");
const filter=ref('');
const model = ref('');
const rootNode = computed(()=>{
return store.currentFlow?.getRoot();
@@ -148,11 +148,11 @@ const minPanelWidth=computed(()=>{
if(store.currentFlow && root){
return store.currentFlow?.getColumns(root) * 300 + 'px';
}else{
return "300px";
return '300px';
}
});
const fixedLeftPosition = computed(()=>{
return drawerLeft.value?"300px":"0px";
return drawerLeft.value?'300px':'0px';
});
const addNode = (node: IActionNode, inputPoint: string) => {
@@ -195,12 +195,12 @@ const onDeleteAllNextNodes = (node: IActionNode) => {
store.currentFlow?.removeAllNext(node.id);
}
const closeDg = (val: any) => {
console.log("Dialog closed->", val);
console.log('Dialog closed->', val);
if (val == 'OK' && appDg?.value?.selected?.length > 0) {
const data = appDg.value.selected[0];
const actionProps = JSON.parse(data.property);
const outputPoint = JSON.parse(data.outputPoints);
const action = new ActionNode(data.name, data.desc, "", outputPoint, actionProps);
const action = new ActionNode(data.name, data.desc, '', outputPoint, actionProps);
store.currentFlow?.addNode(action, prevNodeIfo.value.prevNode, prevNodeIfo.value.inputPoint);
}
}
@@ -227,8 +227,8 @@ const onDeploy = async () => {
if (store.appInfo === undefined || store.flows?.length === 0) {
$q.notify({
type: 'negative',
caption: "エラー",
message: `設定されたフローがありません。`
caption: 'エラー',
message: '設定されたフローがありません。'
});
return;
}
@@ -238,16 +238,16 @@ const onDeploy = async () => {
deployLoading.value = false;
$q.notify({
type: 'positive',
caption: "通知",
message: `デプロイを成功しました。`
caption: '通知',
message: 'デプロイを成功しました。'
});
} catch (error) {
console.error(error);
deployLoading.value = false;
$q.notify({
type: 'negative',
caption: "エラー",
message: `デプロイが失敗しました。`
caption: 'エラー',
message: 'デプロイが失敗しました。'
})
}
return;
@@ -258,7 +258,7 @@ const onSaveActionProps=(props:IActionProperty[])=>{
store.activeNode.actionProps=props;
$q.notify({
type: 'positive',
caption: "通知",
caption: '通知',
message: `${store.activeNode?.subTitle}の属性を設定しました。(保存はされていません)`
});
}
@@ -291,7 +291,7 @@ const onSaveFlow = async () => {
$q.notify({
type: 'negative',
caption: 'エラー',
message: `選択中のフローがありません。`
message: '選択中のフローがありません。'
});
return;
}
@@ -301,7 +301,7 @@ const onSaveFlow = async () => {
saveLoading.value = false;
$q.notify({
type: 'positive',
caption: "通知",
caption: '通知',
message: `${targetFlow.getRoot()?.subTitle}のフロー設定を保存しました。`
});
} catch (error) {
@@ -309,7 +309,7 @@ const onSaveFlow = async () => {
saveLoading.value = false;
$q.notify({
type: 'negative',
caption: "エラー",
caption: 'エラー',
message: `${targetFlow.getRoot()?.subTitle}のフローの設定の保存が失敗しました。`
})
}
@@ -324,7 +324,7 @@ const onSaveAllFlow= async ()=>{
$q.notify({
type: 'negative',
caption: 'エラー',
message: `設定されたフローがありません。`
message: '設定されたフローがありません。'
});
return;
}
@@ -338,8 +338,8 @@ const onSaveAllFlow= async ()=>{
}
$q.notify({
type: 'positive',
caption: "通知",
message: `すべてのフロー設定を保存しました。`
caption: '通知',
message: 'すべてのフロー設定を保存しました。'
});
saveLoading.value = false;
}catch (error) {
@@ -347,8 +347,8 @@ const onSaveAllFlow= async ()=>{
saveLoading.value = false;
$q.notify({
type: 'negative',
caption: "エラー",
message: `フローの設定の保存が失敗しました。`
caption: 'エラー',
message: 'フローの設定の保存が失敗しました。'
});
}
}

View File

@@ -27,23 +27,23 @@ import ActionSelect from 'components/ActionSelect.vue';
import PropertyPanel from 'components/right/PropertyPanel.vue';
const rootNode:RootAction =new RootAction("app.record.create.submit","レコード追加画面","保存するとき");
const rootNode:RootAction =new RootAction('app.record.create.submit','レコード追加画面','保存するとき');
const actionFlow: ActionFlow = new ActionFlow(rootNode);
const saibanProps:IActionProperty[]=[{
component:"InputText",
component:'InputText',
props:{
displayName:"フォーマット",
modelValue:"",
name:"format",
placeholder:"フォーマットを入力してください",
displayName:'フォーマット',
modelValue:'',
name:'format',
placeholder:'フォーマットを入力してください',
}
},{
component:"FieldInput",
component:'FieldInput',
props:{
displayName:"採番項目",
modelValue:"",
name:"field",
placeholder:"採番項目を選択してください",
displayName:'採番項目',
modelValue:'',
name:'field',
placeholder:'採番項目を選択してください',
}
}];
@@ -91,7 +91,7 @@ const onDeleteAllNextNodes=(node:IActionNode)=>{
refFlow.value.removeAllNext(node.id);
}
const closeDg=(val :any)=>{
console.log("Dialog closed->",val);
console.log('Dialog closed->',val);
}
</script>

View File

@@ -1,6 +1,5 @@
<template>
<q-page>
<div class="q-pa-md">
<div class="q-gutter-sm row items-start">
<q-breadcrumbs>
@@ -16,18 +15,18 @@
</template>
<script setup lang="ts">
import {ref} from 'vue'
import { ref } from 'vue';
import DocUploader from 'components/DocUpload.vue';
import AppInfo from 'components/AppInfo.vue';
import { AppSeed } from 'src/components/models';
interface AppInfo {
app:string,
revision:string
app: string;
revision: string;
}
const appseed = withDefaults(defineProps<AppSeed>(), {
app:''
app: '',
});
// const appseed = defineProps<AppSeed>();
@@ -38,6 +37,4 @@ function onAppUploaded(responseText :string){
let json: AppInfo = JSON.parse(responseText);
props.value = json;
}
</script>

View File

@@ -61,7 +61,7 @@ import { useAuthStore } from 'stores/useAuthStore';
let email = ref('');
let password = ref('');
let visibility = ref(false);
let passwordFieldType = ref('password');
let passwordFieldType = ref<'text'|'password'>('password');
let visibilityIcon = ref('visibility');
const required = (val:string) => {
return (val && val.length > 0 || '必須項目')

View File

@@ -66,8 +66,8 @@ interface Props {
actions: string[];
}
const props = withDefaults(defineProps<Props>(), {
title: "ルールエディター",
actions: () => ["フィールド制御", "一覧画面", "その他"]
title: 'ルールエディター',
actions: () => ['フィールド制御', '一覧画面', 'その他']
});
function onItemClick(evt: Event) {
return;

View File

@@ -250,7 +250,7 @@ const isOwner = (row: IDomainOwnerDisplay) => row.owner.id === Number(authStore.
const getDomain = async (filter?: (row: IDomainOwnerDisplay) => boolean) => {
loading.value = true;
const { data } = await api.get<{data:IDomain[]}>(`api/domains`);
const { data } = await api.get<{data:IDomain[]}>('api/domains');
rows.value = data.data.map((item) => {
return {
id: item.id,
@@ -309,7 +309,7 @@ const deleteDomain = () => {
editId.value = 0; // set in removeRow()
};
function editRow(row) {
function editRow(row: any) {
isCreate.value = false
editId.value = row.id;
// tenantid.value = row.tenantid;
@@ -350,7 +350,7 @@ const onSubmit = () => {
'ownerid': authStore.userId || ''
}
// for search: api.put(`api/domain`)、api.post(`api/domain`)
api[method].apply(api, [`api/domain`, param]).then(async (resp: any) => {
api[method].apply(api, ['api/domain', param]).then(async (resp: any) => {
const res = resp.data;
if (res.data.id === currentDomainId.value && !res.data.is_active) {
await authStore.setCurrentDomain();

View File

@@ -14,7 +14,7 @@
<q-btn class="q-mx-none" color="primary" label="追加" @click="clickAddDomain()" />
<q-space />
<div class="row q-gutter-md">
<q-input borderless dense filled debounce="300" v-model="userDomainTableFilter" placeholder="Search">
<q-input borderless dense filled debounce="300" v-model="userDomainTableFilter" placeholder="検索">
<template v-slot:append>
<q-icon name="search" />
</template>
@@ -114,7 +114,7 @@ const addUserDomainFinished = async (val: string) => {
const selected = addDomainRef.value.selected;
if (val == 'OK' && selected.length > 0) {
addUserDomainLoading.value = true;
const { data } = await api.post(`api/userdomain`, {
const { data } = await api.post('api/userdomain', {
userid: authStore.userId,
domainid: selected[0].id,
});
@@ -169,9 +169,9 @@ const isActive = computed(() => (id: number) => {
const getDomain = async (userId? : string) => {
rowIds.clear();
const resp = await api.get(`api/defaultdomain`);
const resp = await api.get('api/defaultdomain');
activeDomainId.value = resp?.data?.data?.id;
const domainResult = userId ? await api.get(`api/domain?userId=${userId}`) : await api.get(`api/domain`);
const domainResult = userId ? await api.get(`api/domain?userId=${userId}`) : await api.get('api/domain');
const domains = domainResult.data as any[];
rows.value = domains.sort((a, b) => a.id - b.id).reduce((acc, item) => {
rowIds.add(item.id);

View File

@@ -54,8 +54,8 @@ const mouseenter = (event: Event) => {
let oDivs = oDiv1?.getElementsByClassName('add');
if (oDivs.length === 0) {
let oDiv2 = document.createElement('div');
oDiv2.className = "add";
oDiv2.setAttribute("style", "display:table-row;height:inherit;position: absolute;left:calc(50% - 19px);");
oDiv2.className = 'add';
oDiv2.setAttribute('style', 'display:table-row;height:inherit;position: absolute;left:calc(50% - 19px);');
oDiv2.innerHTML = oAdd;
oDiv1?.append(oDiv2);
}

View File

@@ -16,7 +16,7 @@ const routes: RouteRecordRaw[] = [
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/IndexPage.vue') },
{ path: '', component: () => import('pages/IndexPage.vue'), props: { app: '' } },
{ path: 'ruleEditor', component: () => import('pages/RuleEditor.vue') },
{ path: 'flow', component: () => import('pages/testFlow.vue') },
{ path: 'FlowChartTest', component: () => import('pages/FlowChartTest.vue') },

View File

@@ -103,5 +103,5 @@ function appToAppDisplay(app: IManagedApp) {
}
function formatDate(data: string) {
return date.formatDate(data, 'YYYY/MM/DD HH:mm');
return date.formatDate(new Date(data + 'Z'), 'YYYY/MM/DD HH:mm');
}

View File

@@ -64,7 +64,7 @@ export const useAuthStore = defineStore('auth', {
params.append('username', username);
params.append('password', password);
try {
const result = await api.post(`api/token`, params);
const result = await api.post('api/token', params);
// console.info(result);
this.token = result.data.access_token;
const tokenJson = jwtDecode<{sub: number, tenant: string, roles: string}>(result.data.access_token);
@@ -86,7 +86,7 @@ export const useAuthStore = defineStore('auth', {
}
},
async loadCurrentDomain() {
const resp = await api.get<IResponse<IDomain>>(`api/defaultdomain`);
const resp = await api.get<IResponse<IDomain>>('api/defaultdomain');
const activedomain = resp?.data?.data;
if (!activedomain) {
this.currentDomain = {} as IDomainInfo;
@@ -99,13 +99,13 @@ export const useAuthStore = defineStore('auth', {
}
},
async loadUserInfo() {
const resp = (await api.get<IResponse<IUser>>(`api/v1/users/me`))?.data?.data;
const resp = (await api.get<IResponse<IUser>>('api/v1/users/me'))?.data?.data;
this.userInfo = userToUserRolesDisplay(resp)
},
async loadPermission() {
this.permissions = {} as IPermissions;
if (this.isSuperAdmin) return;
const resp = (await api.get<IResponse<IPermission[]>>(`api/v1/userpermssions`)).data.data;
const resp = (await api.get<IResponse<IPermission[]>>('api/v1/userpermssions')).data.data;
resp.forEach((permission) => {
this.permissions[permission.link] = permission.menu;
this.permissions[permission.privilege] = permission.function;

View File

@@ -129,7 +129,7 @@ export class ActionNode implements IActionNode {
id: string;
name: string;
get title(): string {
const prop = this.actionProps.find((prop) => prop.props.name === "displayName");
const prop = this.actionProps.find((prop) => prop.props.name === 'displayName');
return prop?.props.modelValue;
};
get subTitle(): string {
@@ -138,7 +138,7 @@ export class ActionNode implements IActionNode {
//変数名
get varName():IProp|undefined{
const prop = this.actionProps.find((prop) => prop.props.name === "verName");
const prop = this.actionProps.find((prop) => prop.props.name === 'verName');
return prop?.props;
}

View File

@@ -136,7 +136,7 @@ export class KintoneEventManager {
const flows:IActionFlow[]=[];
for (const screen of this.screens) {
for (const event of screen.events) {
if (event.header === "EVENT") {
if (event.header === 'EVENT') {
const eventNode = event as IKintoneEvent;
if(eventNode.flowData!==undefined){
flows.push(eventNode.flowData);
@@ -144,7 +144,7 @@ export class KintoneEventManager {
}else if (event.header === 'EVENTGROUP' || event.header === 'CHANGE') {
const eventGroup = event as IKintoneEventGroup;
eventGroup.events.forEach((ev) => {
if (ev.header === "EVENT" || ev.header === "DELETABLE") {
if (ev.header === 'EVENT' || ev.header === 'DELETABLE') {
const eventNode = ev as IKintoneEvent;
if(eventNode.flowData!==undefined){
flows.push(eventNode.flowData);