あなたの習慣について教えてください!!

Access2010を使用しています。
フォーム:ログインにID、PASSのテキストBOXを設け、フォーム:メニューのコマンドボタンをID別に使用不可にしています。問題は、メニューからさらに製品仕様書の一覧フォームを開き、指定の製品の詳細フォームを開くのですが、詳細フォームにログインで入力したIDを使用し、使用不可コマンドボタンを作りたいのです。

ログインボタン
Dim Res
If IsNull(Me.ID) Then
MsgBox "IDを入力してください"
Me.ID.SetFocus
Exit Sub
End If

If IsNull(Me.パスワード) Then

MsgBox "パスワードを入力してください"
Me.パスワード.SetFocus
Exit Sub
End If

Res = DLookup("パスワード", "talogin", "ID='" & Me.ID & "'")
If IsNull(Res) Then
MsgBox "該当するIDはありません。正しいIDを入力してください。"
Me.ID.SetFocus
Exit Sub
End If

If Res = Me.パスワード Then

DoCmd.OpenForm "メニュー" '----ID,pass合致でフォームを開く。

Forms!メニュー!ログイン名 = Me.ID

Else

MsgBox "パスワードが異なります。", vbOKOnly + vbCritical
Me.パスワード.SetFocus
End If
End sub

ログインボタンで、テーブルtaEnabledを参照。不可リストに書き込んだボタン名のみ、使用不可にする。
メニュー画面にログイン名を表示。

Private Sub Form_Open(Cancel As Integer)
Dim AID As Long
Dim Var As Variant
AID = Nz(DLookup("権限ID", "taLogin", "ID ='" & Forms!ログイン!ID & "'"), 0)


For Each Var In Split(DLookup("不可リスト", "taEnabled", "権限ID =" & AID), ",")
Me.Controls(Var).Enabled = Flase
Next Var

End Sub

この後、フォーム:メニューに設置の各帳票・単表フォームを開いた時に、再度IDを使用し、表示したフォーム内のコマンドボタンを使用不可にしたいので、皆様のお力をお貸し下さい。

A 回答 (4件)

#3です



申し訳ありません。
各フィールドの型を記述していませんでしたね。

テーブル「TA」では、LV(数値型/長整数)、その他はテキスト型
テーブル「TB」では、LV(数値型/長整数)、SLV はテキスト型
 (テーブル「TB」の主キーは LV)

たぶん、この型があっていなかったので、コンボボックスの

SELECT TA.ID, TA.名前, TA.PWD, TB.SLV FROM TA
LEFT JOIN TB ON TA.LV = TB.LV;

が取得できていなかった可能性があります。
デザインで、この「値集合ソース」の表示部分をクリックすると、
右側の SELECT・・・・が反転表示になると思います。
その状態で、右側の[・・・]をクリックすると、クエリのデザインの様な表示になります。
「F_LOGIN:クエリ ビルダ」の表示部分で、マウス右クリックし、
「データシート」表示にしてデータがどう並んでいるか確認してみます。
OK なら保存して終了です。

標準モジュールへの変更は#3の通りです。
確認するフォーム「F_MENU」は#2から変更なしです。
(コントロール名に _ が付いている状態のものです)

また、私が確認したのは、テーブルを以下の様に設定していました。

テーブル「TA」
ID  名前  PWD LV
A1234 AAAA 1234 1
B1234 BBBB 1234 2
C1234 CCCC 1234 3
D1234 DDDD 1234 6

テーブル「TB」
LV SLV
1 ,2,3,4,
2 ,2,
3 ,3,5,
4 ,4,
5 ,5,1,

これで確認できると思います。
(上記では D1234 さんがオールマイティ)

繰り返しになりますが、
他のフォームでも同じように Enabled 設定したかったら、そのフォームに

Private Sub Form_Load()
  Call FrmLevelSet(Me)
End Sub

を記述するだけです。ただ、コントロール名は命名規則に従っている事が前提です。
また、
Call FrmLevelInit ' 初期化 を記述するのは、ログアウトする直前の最後の最後です。
途中初期化してしまうと情報が消えてしまうので、再ログインが必要になります。



2)の方法で良かったんでしょうか。
押しつけるつもりはありませんので、良い方法が見つかればそちらの方で・・・・


※ 2)はレベルをコントロール名に持っていましたが、この部分にタグ(Tag)を使う
  っていう方法もあります。雰囲気わかっていただけるかと思います。
2)で「btn_4」となっていたらタグ(Tag)に ,4, を設定しておいてコントロール名は自由に

>  i = InStr(.Name, "_")
>  If (i > 0) Then
>    If (InStr(sUserLevel, "," & Mid(.Name, i + 1) & ",") > 0) Then
>      .Enabled = False
>    End If
>  End If

部分を

  If (Len(.Tag) > 0) Then
    If (InStr(sUserLevel, .Tag) > 0) Then
      .Enabled = False
    End If
  End If

に変更するだけです。
この方法では、タグ(Tag)は書き換えれませんが、
テーブル「TB」の SLV 変更で、途中からでも変更要求には対応できます。


おかしな所等ありましたら、補足いただければと・・・
    • good
    • 0
この回答へのお礼

30246kikuさん。
長い間ご指導頂きありがとうございました。
2)の方法で、一度完成したのですが、やはりSLV設定がシビアでExcelで制限表を作り管理するはめになりました。
今後の事を考え、1)の方法を再度よく見直しようやく理解できましたので、1)に変更しました。
ものすごく管理が楽になり、ものの数分で制御がすべて完了しました。
初めに1)のtagが全く理解できず、敬遠してしまい(自爆)、2)に走ってしまいました。
ご丁寧に説明までして頂き、考え方が理解できました。
また、何かあれば宜しくお願い致します。

感謝!!!

お礼日時:2012/10/18 16:17

#2です



遅くなりました。

はじめに、遠回りなことを記述して申し訳ありませんでした。
権限・・・所属ごとではなく、階級ごとと思いこんでしまいました。
なので、#1の後半について詳しく書いてしまいました。

新規DB で確認されたようですので、そこに修正を加えていきたいと思います。

確認された DB を、エクスプローラでコピーしておきます。
コピー元はいじらずにそのままにして、コピーした方に手を入れていきます。

> 今回の手法は、レベルに応じて4以下の3,2,1が制限されますが、以下ではなく4だけの制限とか、3だけの制限に出来ますか?

標準モジュールに記述した、Public Sub FrmLevelSet(frm As Form) 内の

        If (CLng(Mid(.Name, i + 1)) > iUserLevel) Then

        If (CLng(Mid(.Name, i + 1)) = iUserLevel) Then
とすると、その値だけが Enabled = False になります。

※ ただし、複数部署で1つのコントロールを Enabled = False したい要求には応えられません。

この要求に応えるため、修正量の少ないものから2つ紹介します。


1)権限情報をコントロール名「 _レベル 」から、タグ(Tag)へ変更

元 DB をコピーして、コピーした方を使います。

#2では権限レベルを「btn1_1」「btn3_2」の様にコントロール名に持たせていましたが、
タグ(Tag)に持たせるようにします。
タグ(Tag)はそのコントロールをデザインで見た時、プロパティで設定することが出来ます。

「F_MENU」をデザインで開いて、「btn1_1」をクリックします。
プロパティの「タグ」に ,1, と記述します。(レベルの数字を , カンマで挟むように)
他のコントロールも同じように設定してみます。
レベルを判別(Enabled = False)する必要のないコントロールの「タグ」は空白のまま。
コントロール名は見なくなるので、好きなように変更しておきます。
「btn3_2」だった「タグ」に ,2,3, とカンマ区切りで 2 と 3 を記述しておきます。
この記述が、2 の人、3 の人なら Enabled = False にする指定となります。

フォーム上の変更はこれで終わりです。
標準モジュールに記述していた Public Sub FrmLevelSet(frm As Form) 部分を以下に変更します。

' 指定されたフォームのコントロールのタグをチェック
Public Sub FrmLevelSet(frm As Form)
  Dim ctl As Control
  Dim i As Long

  For Each ctl In frm.Controls
    With ctl
      i = InStr(.Tag, "," & iUserLevel & ",")
      If (i > 0) Then .Enabled = False
    End With
  Next
End Sub

ここで何をやっているかですが、タグ(Tag)に設定された文字列に、
自分のレベルがありますか・・・・これを InStr で確認しています。
タグ(Tag)に ,2,3, があったとして、自分のレベルが 2 だとすると、
InStr(",2,3,", ",2,") で、戻り値は 0 ではありません(1 が得られます)
なので Enabled = False することに

また、ログインをすり抜けた時の 0 をどう扱うか
( ,0, を記述を追加しておけば Enabled = False にできます)

この方法での修正は以上です。
フォーム「F_LOGIN」を起動して、いろいろな人で入って確認してみます。
タグ(Tag)に ,2,3, を設定していたものがあったら、
ID「A1234」「B1234」両方で Enabled = False になると思います。

※ タグ(Tag)を別の用途で使われていたのなら、この方法は使えないものになります。
 また、タグ(Tag)はデザイン時に設定するので、運用途中で対象外にしたい・・・できません
 (accde とかなら)


2)ご質問時の構成に近づける

元 DB をコピーして、コピーした方を使います。

テーブル「TA」

ID  名前  PWD LV
A1234 AAAA 1234 3
B1234 BBBB 1234 2
C1234 CCCC 1234 5
D1234 DDDD 1234 4

テーブル「TB」

LV SLV
1 ,1,
2 ,2,
3 ,3,
4 ,4,
5 ,5,

テーブル名は異なりますが、ご質問時の構成に近いと思います。
テーブル「SLV」部分が、コントロール名の羅列(カンマ区切り)になっていたと思われます。
ご質問時の場合、#1での前者でできると思いますが、
Enabled = False にする記述を各フォームで処理する提示となっていたので、
#2同様に関数1つ呼ぶだけで可能にしてみたいと思います。

フルのコントロール名の羅列も悪くはないと思いますが、いろいろフォームを作っていくと
同じコントロール名を使ってしまう事が多々あります。
そこで、#2と同じ命名規則を用いることとします。

部署 → レベル
1 × ○ ○ × × ○
2 ○ × ○ × ○ ×
3 ○ ○ × ○ × ×
LV 1 2 3 4 5 6

例えば、上記の様なパターンを作って、× のところを Enabled = False にする。
部署 1 2 を Enabled = False にする時のコントロール名は「btn_4」にするとか・・・

このパターンを元にテーブル「TB」を3部署について記述してみると

LV SLV
1 ,1,4,5,
2 ,2,4,6,
3 ,3,5,6,

とか、もちろん無い組合せまで作る必要はありません。
( SLV 内の各数字はカンマで挟んでおきます)

この方法にする時の修正は
フォーム「F_LOGIN」内コンボボックスの値集合ソース記述を

SELECT TA.ID, TA.名前, TA.PWD, TB.SLV FROM TA
LEFT JOIN TB ON TA.LV = TB.LV;

これでフォーム「F_LOGIN」の修正は終了です。


標準モジュール内の記述を修正していきます。(変更する部分だけを記述)

Dim sUserLevel As String ' ユーザの権限レベル

Public Sub FrmLevelInit(ParamArray v())
'  On Error Resume Next
  If (IsMissing(v)) Then
    sIdSave = ""
    sNameSave = ""
    sUserLevel = ""
  Else
    sIdSave = v(0)
    sNameSave = v(1)
    sUserLevel = v(2)
  End If
End Sub

Public Sub FrmLevelSet(frm As Form)
  Dim ctl As Control
  Dim i As Long

  For Each ctl In frm.Controls
    With ctl
      i = InStr(.Name, "_")
      If (i > 0) Then
        If (InStr(sUserLevel, "," & Mid(.Name, i + 1) & ",") > 0) Then
          .Enabled = False
        End If
      End If
    End With
  Next
End Sub

ここでやっているのは、ユーザのレベルが文字列になったので、String に変更
コントロール名に「 _ 」があったら、ユーザレベルの文字列に _ 以降の文字列があるか・・・
例えば、ユーザレベル文字列が ",1,4,5," だった時、コントロール名「btn_4」では、
InStr(",1,4,5,", ",4,") として、あるかないか確認します。
あったら Enabled = False に・・・

オールマイティの人を作る時には、
テーブル「TA」の LV に、テーブル「TB」の LV に無い数値を設定すれば可能になります。

これで修正完了です。


1) 2)は、情報の置き場が異なるだけです。
一連の Enabled = False にする情報 ",2,3," をタグに・・・1) ユーザのレベルに・・・2)
何を元に、テーブルのLV・・・1) コントロール名の _ 以降・・・2)


こんな感じで良かったでしょうか。不明な点あれば補足してください。

この回答への補足

何度もすみません。
2)の方法に変更したのですが、どうしても”IDパスワードが違います”となり、ログインできません。
何度も見直しているのですが、どうしても原因がわかりません。
2点だけそれ程関係ないとは思いますが、確認内容です。

確認内容:1)テーブルTB LV数値型 SLVテキスト型 2)テーブルTA LVテキスト型に変更?

宜しくお願いします。

補足日時:2012/10/16 16:02
    • good
    • 0

#1です



詳しくと言うより、新規で作って確認してみましょう。
以下に記述するのは、#1での後者の方法になります。
やり方を押しつけるわけではないので、不可リストの方が良ければ言ってください。
必要なら補足してください。今回、字数制限めいっぱいで、後者しか記述できません。

・新規 accdb を開きます。
・ログイン情報があるテーブル「TA」を作ります。
フィールドは「ID」「名前」「PWD」「LV」の4つ(「LV」のみ数値型、他はテキスト型)
「ID」は主キー
以下のデータを入れておきます。

ID  名前  PWD LV
A1234 AAAA 1234 3
B1234 BBBB 1234 2
C1234 CCCC 1234 5
D1234 DDDD 1234 4

・ログイン用フォーム「F_LOGIN」作成
デザインから作っていきます。
ポップアップ:はい、レコードセレクタ/移動ボタン:いいえ、にしておきます。
ID、パスワード用のテキストボックス「txID」「txPWD」を配置
ID、パスワード照合用に非表示のコンボボックス「cbx1」を配置しプロパティで
可視: いいえ
列数: 4
値集合ソース: SELECT TA.ID, TA.名前, TA.PWD, TA.LV FROM TA;
値集合タイプ: テーブル/クエリ
連結列: 1
入力チェック: はい

ログイン用、閉じる用コマンドボタン「btn1」「btn2」を配置
「btn1」の「クリック時」を[イベント プロシージャ]にし、横の[・・・]クリックします。

Private Sub btn1_Click()

End Sub

が表示されるので、この3行を以下と置き換えます。
(コピー&貼り付けされる時には、OKWave を推奨、現在、教えてgoo では、行頭に不要なスペースが付加されます)


Dim iErrCnt As Integer

Private Sub Form_Load()
  iErrCnt = 0
End Sub

Private Sub btn1_Click()
  Dim sErr As String
  Dim bErr As Boolean

  sErr = ""
  If (IsNull(Me.txID)) Then
    sErr = "IDを入力してください" & vbCrLf
  End If
  If (IsNull(Me.txPWD)) Then
    sErr = sErr & "パスワードを入力してください"
  End If
  If (Len(sErr) > 0) Then
    MsgBox sErr
    Exit Sub
  End If

  bErr = False
  Me.cbx1 = Me.txID ' ID をコンボボックスと照合
  If (IsNull(Me.cbx1.Column(0))) Then bErr = True
  If (bErr = False) Then
    ' ID が存在したのでパスワードの照合(大文字小文字区別あり)
    If (StrComp(Me.txPWD, Me.cbx1.Column(2), vbBinaryCompare) = 0) Then
      ' パスワードも一致したので ID, 名前, 権限レベルの設定
      Call FrmLevelInit(Me.cbx1.Column(0) _
              , Me.cbx1.Column(1) _
              , Me.cbx1.Column(3))
      ' F_MENU を起動して自分を閉じる
      DoCmd.OpenForm "F_MENU"
      DoCmd.Close acForm, Me.Name, acSaveNo
    Else
      bErr = True
    End If
  End If
  If (bErr = True) Then
    iErrCnt = iErrCnt + 1
    If (iErrCnt >= 3) Then
      MsgBox "ログインできませんでした"
      Call btn2_Click
    Else
      MsgBox "ID/パスワードが違います"
      Me.txID.SetFocus
    End If
  End If
End Sub

Private Sub btn2_Click()
  DoCmd.Close acForm, Me.Name, acSaveNo
End Sub


3回ログインに失敗したらフォームを閉じます。
また、ID、パスワードどちらを間違ったのか教えません。
これで「F_LOGIN」は完成です。

・メニュー用フォーム「F_MENU」作成
デザインから作っていきます。
ポップアップ:はい、レコードセレクタ/移動ボタン:いいえ、にしておきます。
ID表示用のテキストボックスを配置し、コントロールソースに =loginid() を設定
名前表示用のテキストボックスを配置し、コントロールソースに =loginname() を設定

確認用のコマンドボタンを6つ配置
名前を「btn1_1」「btn3_2」「btn5_3」「btn7」「btn9_4」「btn_5」に変更(標題も同じに)
「btn7」以外が権限レベルによって Enabled 処理対象になります。
閉じるコマンドボタン「btn10」を配置

「btn10」の「クリック時」を[イベント プロシージャ]にし、横の[・・・]クリックします。
前フォームと同様に、表示された3行を以下と置き換えます。


Private Sub Form_Load()
  Call FrmLevelSet(Me)
End Sub

Private Sub btn10_Click()
  DoCmd.Close acForm, Me.Name, acSaveNo
End Sub

Private Sub Form_Close()
  Call FrmLevelInit ' 初期化
End Sub


Load 時に、Enabled 設定の関数を呼び出します。
閉じられる時、ID、名前、権限レベルを初期化します。
(初期化は、ログアウト状態になる時に)
これで「F_MENU」は完成です。

これらのフォームが動くための以下処理を標準モジュールに記述します。


Dim sIdSave As String ' ID
Dim sNameSave As String ' 名前
Dim iUserLevel As Long ' 権限レベル

Public Sub FrmLevelInit(ParamArray v())
  On Error Resume Next
  If (IsMissing(v)) Then
    sIdSave = ""
    sNameSave = ""
    iUserLevel = 0
  Else
    sIdSave = v(0)
    sNameSave = v(1)
    iUserLevel = CLng(v(2))
  End If
End Sub

' 指定されたフォームのコントロール名をチェック
' 「 _ 」があったら末尾のレベル確認 & Enabled 設定
Public Sub FrmLevelSet(frm As Form)
  Dim ctl As Control
  Dim i As Long

  For Each ctl In frm.Controls
    With ctl
      i = InStr(.Name, "_")
      If (i > 0) Then
        If (CLng(Mid(.Name, i + 1)) > iUserLevel) Then
          .Enabled = False
        End If
      End If
    End With
  Next
End Sub

Public Function LoginId() As String   ' ID を返す
  LoginId = sIdSave
End Function

Public Function LoginName() As String ' 名前を返す
  LoginName = sNameSave
End Function


記述後保存して、「F_LOGIN」を起動して動きを見てみます。
フォームのデザインと実行時の画面は、添付図の様になります。
(添付図右下は、直に「F_MENU」を起動した時のもの)

フォーム「F_MENU」内のコントロール名の権限レベル部分や、
テーブル「TA」内 LV を変更して、いろいろやってみてください。

他のフォームでも同じように Enabled 設定したかったら、そのフォームに

Private Sub Form_Load()
  Call FrmLevelSet(Me)
End Sub

を記述するだけです。


※ 2000 / 2003 / 2007 では確認済み:(2010 は持ってません)



こういう事ではなかった?
「VBAの書き方を教えてください。」の回答画像2

この回答への補足

30246kikuさん、ありがとうございます!!!
簡単に出来ました!
やっていることはわかりましたが、全く書ける気がしなです.....(汗)

大変お手数ではありますが、もう一点教えてください。
今回の手法は、レベルに応じて4以下の3,2,1が制限されますが、以下ではなく4だけの制限とか、3だけの制限に出来ますか?
目的は、全部署使用するメニューですので部署単位で制限をかけたいのです。
レベルよりも部署コードみたいなものです。
コード1の営業部は見積書や新規獲得売上げなどを使いますが、コード2製品管理部は在庫表しか使いません。コード3の製造部は見積書も、在庫表も使いますが、新規獲得売上げは使いません。

すみませんが、もう少しお付き合い頂けると助かります。

補足日時:2012/10/15 16:29
    • good
    • 0

以下は1つの案にすぎませんので・・・また、動作検証もしていないので・・・


標準モジュールを使う方法になります。

その人の権限による不可リストをメモリ上で管理します。
パスワードも一致したところで、

Call LoginInfoOn(Me.ID)

で、不可リストを Dictionary で管理するように・・・
その後、コントロールの Enabled 設定時に、

Me("一覧表示").Enabled = LoginInfo("一覧表示")

の様に使います。複数のコントロール名に対して処理する時には

Dim v As Variant

For Each v In Array("一覧表示","登録","更新")
  Me(v).Enabled = LoginInfo(CStr(v))
Next

とか


以下を標準モジュールに記述しておきます。

Dim dic As Object
Dim sIdSave As String

Public Sub LoginInfoOn(sNo As String)
  Dim rs As ADODB.Recordset
  Dim sSql As String
  Dim v As Variant

  sIdSave = sNo
  Set dic = CreateObject("Scripting.Dictionary")
  sSql = "SELECT Q1.不可リスト FROM taEnabled AS Q1 " _
    & "INNER JOIN taLogin AS Q2 ON Q1.権限ID = Q2.権限ID " _
    & "WHERE Q2.ID = '" & sNo & "';"
  Set rs = CurrentProject.Connection.Execute(sSql)
  If (Not rs.EOF) Then
    For Each v In Split(rs(0).Value, ",")
      dic.Item(CStr(v)) = Null
    Next
  End If
  Set rs = Nothing
End Sub

Public Sub LoginInfoOff()
  sIdSave = ""
  Set dic = Nothing
End Sub

Public Function LoginInfo(sName As String) As Boolean
  LoginInfo = True
  If (dic Is Nothing) Then Exit Function
  LoginInfo = Not dic.Exists(sName)
End Function

Public Function LoginId() As String
  LoginId = sIdSave
End Function


ログインIDを表示したい時には、そのコントロールのコントロールソースに
=LoninId()
を記述すれば表示されると思います。

上記は ADO を使っているので DAO の場合は

  Dim rs As ADODB.Recordset

  Set rs = CurrentProject.Connection.Execute(sSql)

部分を

  Dim rs As DAO.Recordset

  Set rs = CurrentDb.OpenRecordset(sSql)

とすれば良いかも・・・
未検証ですので、雰囲気だけでも伝わればと・・・



また、以下の様な考え方もあるかも。

権限レベルを 1 ~ 9 までの数値と仮定します。
コントロール名の命名規則で、権限レベルにより Enabled を制御してみます。
「btn1_3」とか「btn2_5」とか「btn8」とか・・・・
「 _ 」の後ろの数字を権限レベルとします。
「 _ 」が存在しない場合は何もしない。
権限レベル 3 の人には 「 _1」~「 _3」までを Enabled = True とする・・・
(「 _4」以降を Enabled = False とする)

このような命名規則にしておけば、
・違うフォームを作ったから・・・・
不可リストにフルのコントロール名を追加しなくても済む等、楽になるような気がします。
例えば、以下の様なものを 標準モジュールに記述しておきます。

Dim sIdSave As String ' ユーザID
Dim iUserLevel As Long ' ユーザの権限レベル

Public Sub FrmLevelSet(frm As Form)
  Dim ctl As Control
  Dim i As Long

  For Each ctl In frm.Controls
    With ctl
      i = InStr(.Name, "_")
      If (i > 0) Then
        If (CLng(Mid(.Name, i + 1)) > iUserLevel) Then
          .Enabled = False
        End If
      End If
    End With
  Next
End Sub


sIdSave 、iUserLevel はパスワードが一致した後で、誰かが設定したとして・・・
各フォームでは、Open や Load 時に、

Call FrmLevelSet(Me)

と呼ぶだけになります。(前述同様に動作未検証)

人に与える権限を 1 ~ としておいたのは、
何らかのルートでログインせずにフォームを起動された場合、
標準モジュールのユーザ権限レベルは 0 になっているので、
フォームを起動できたとしても、
レベルを設定したコントロールは Enabled = False にできる
チョッとしたメリットがあるかも。

※ フォーカスのあるコントロールに対して Enabled = False できないので注意してください。

この回答への補足

30246kikuさん、ありがとうございます。
すみません・・・先に記載しておけばよかったのですが、VBA初心者になります。
提案して頂いた内容が、理解できませんでした。
できれば、理解しやすい手法で、もう少し具体的に教えて頂けないでしょうか。
宜しくお願いします。

補足日時:2012/10/13 08:30
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

関連するカテゴリからQ&Aを探す


おすすめ情報