Plugins › ระบบจัดการ: เมนู ค้นหา และหน้าแก้ไข
เอกสาร/Plugins/ระบบจัดการ: เมนู ค้นหา และหน้าแก้ไข

ระบบจัดการ (admin)

สิ่งที่จะได้เรียนรู้

วิธีปรับระบบจัดการให้ทีมใช้ง่าย: กลุ่มเมนู badge และการปักหมุด, command palette (⌘K) และคีย์ลัด, หน้าแก้ไขที่มีแท็บ ส่วนที่พับได้ แถว และ field ที่แสดงเฉพาะเมื่อเกี่ยวข้อง

อ่านก่อนหน้านี้: การตั้งค่า และ Fields

ทุกอย่างตั้งใน config ระบบจัดการไม่ต้องตั้งค่าแยก แต่ละคนยังพับกลุ่ม ปักหมุด และหุบเมนูได้เอง ค่าเหล่านี้เก็บไว้ในเบราว์เซอร์ของแต่ละคน

เมนู

ระบบจัดการที่เมนูเป็นกลุ่ม: เนื้อหา คลังสื่อ ฟอร์ม และตั้งค่าระบบจัดการที่เมนูเป็นกลุ่ม: เนื้อหา คลังสื่อ ฟอร์ม และตั้งค่า

เมนูเป็นกลุ่มที่พับได้ ลึก 2 ชั้น:

แดชบอร์ด
เนื้อหา            บทความ หน้า… (collection ที่ไม่ได้ระบุกลุ่ม)
คลังสื่อ
ร้านค้า             กลุ่มของ plugin เช่น ร้านค้า
  แคตตาล็อก          สินค้า สินค้าย่อย…
  การขาย            คำสั่งซื้อ การชำระเงิน…
ตั้งค่า
  เว็บไซต์           global, redirects
  ผู้ใช้และสิทธิ์      ผู้ใช้ บทบาท API keys SSO
  ระบบ              Backups อีเมล การส่ง งานเบื้องหลัง Audit log

collection, global และหน้าระบุกลุ่มของตัวเองด้วย admin.group ส่วนกลุ่มของคุณเองประกาศใน admin.nav:

export default defineConfig({
  admin: {
    nav: [
      {
        id: 'library',
        label: { en: 'Library', th: 'ห้องสมุด' },
        icon: 'book-open',
        children: [{ id: 'authors', label: { en: 'Authors', th: 'ผู้เขียน' } }],
      },
    ],
  },
  collections: [
    { slug: 'books', admin: { group: 'library' }, fields: [/* … */] },
    { slug: 'people', admin: { group: 'library.authors' }, fields: [/* … */] },
  ],
  globals: [{ slug: 'footer', admin: { group: 'content' }, fields: [/* … */] }],
})
  • ถ้าไม่ระบุ group collection อยู่ใต้ เนื้อหา และ global อยู่ใต้ ตั้งค่า › เว็บไซต์ ส่วน group: 'settings' ก็คือ ตั้งค่า › เว็บไซต์
  • ใส่ label แทน id (group: 'Library') จะสร้างกลุ่มชื่อนั้นให้ เหมือนเดิม
  • group: false เอา collection, global หรือหน้าออกจากเมนู แต่ยังเข้าถึงได้ผ่านลิงก์
  • order กำหนดตำแหน่งของกลุ่ม: เนื้อหาคือ 0, คลังสื่อ 10, กลุ่มของ plugin ราว 100–200, ตั้งค่า 1000 ส่วนภายในกลุ่ม admin.order ของ collection, global หรือหน้ากำหนดลำดับ (ค่าน้อยมาก่อน) ที่ไม่ได้ตั้งจะตามมาตามลำดับใน config
  • ประกาศ id ของกลุ่มที่ plugin หรือ Easy CMS มีอยู่แล้ว ({ id: 'shop', label: 'Store' } หรือ content) เพื่อเปลี่ยนชื่อ ไอคอน หรือลำดับ
  • แต่ละคนเห็นเฉพาะสิ่งที่ตัวเองเปิดได้ และกลุ่มที่ว่างจะไม่แสดง

ตัวเลขและ badge

collection แสดงจำนวนเอกสาร ปิดได้ด้วย admin.count: false ส่วน badge แสดงสิ่งที่ต้องจัดการแทน นับตามสิ่งที่แต่ละคนอ่านได้:

{
  slug: 'orders',
  admin: {
    badge: { where: { status: { equals: 'paid' } }, label: { en: 'to send', th: 'รอจัดส่ง' } },
  },
}

กลุ่มที่พับอยู่แสดงผลรวมของ badge ในกลุ่ม tone คือ accent (ค่าเริ่มต้น), warning หรือ danger

สิ่งที่แต่ละคนปรับเอง

  • พับกลุ่ม: กลุ่มจะคงสภาพตามที่พับไว้ ส่วนกลุ่มของหน้าที่เปิดอยู่จะกางเสมอ
  • ปักหมุด: ปุ่มหมุดข้างชื่อ collection เอาไปไว้บนสุดของเมนู
  • หุบเมนู: บนจอกว้าง เมนูหุบเหลือไอคอนได้ (ปุ่มด้านล่างเมนู หรือ [) กดไอคอนกลุ่มแล้วรายการจะแสดงข้างๆ
  • สร้าง: ชี้ที่รายการ (หรือกด Tab ไปถึง) จะมีปุ่ม + สำหรับสร้างเอกสาร

ใช้ลูกศรขึ้นลงเลื่อนในเมนูได้ ซ้ายและขวาพับหรือกางกลุ่ม

ค้นหาและคำสั่ง (⌘K)

Command palette: หน้า เอกสาร และคำสั่งที่พบระหว่างพิมพ์Command palette: หน้า เอกสาร และคำสั่งที่พบระหว่างพิมพ์

⌘K (Ctrl+K), / หรือช่องค้นหาด้านบนของเมนู เปิด command palette:

  • ไปที่ collection, global, หน้า หรือการตั้งค่าใดก็ได้ ตามชื่อหรือกลุ่ม
  • สร้าง เอกสารของ collection ที่คุณสร้างได้
  • เอกสาร: ค้นชื่อเรื่องในทุก collection ที่คุณอ่านได้ และค้นด้วย id
  • เปิดล่าสุด: เอกสารที่เปิดล่าสุด แสดงก่อนเริ่มพิมพ์
  • คำสั่ง: เปลี่ยนธีม ภาษา หุบเมนู ดูคีย์ลัด ออกจากระบบ และคำสั่งของคุณเอง:
admin: {
  commands: [{ label: 'ร่างที่รอตรวจ', href: '/collections/posts?status=draft', icon: 'file-text' }],
}
คีย์ลัด
⌘K, /ค้นหาและคำสั่ง
⌘Sบันทึก (ไม่เผยแพร่ร่าง และไม่เอาหน้าออกจากเว็บ)
⌘⇧Pเผยแพร่
[หุบหรือขยายเมนู
g แล้ว dแดชบอร์ด
?คีย์ลัดทั้งหมด

หน้าแก้ไข

admin.layout จัดหน้าแก้ไขตามชื่อ field เป็นแท็บ ส่วนที่พับได้ และแถว เปลี่ยนแค่หน้าตา ข้อมูลยังเป็นตาม field เหมือนเดิม:

{
  slug: 'products',
  admin: {
    layout: [
      { tab: 'ข้อมูล', fields: ['title', 'slug', { row: ['brand', 'category'] }, 'description'] },
      {
        tab: 'การจัดส่ง',
        fields: [
          { row: ['weight', 'width', 'height'] },
          { collapsible: 'ศุลกากร', collapsed: true, fields: ['hsCode', 'origin'] },
        ],
      },
    ],
  },
  fields: [
    { name: 'title', type: 'text' },
    { name: 'brand', type: 'text', admin: { width: '1/3' } },
    { name: 'category', type: 'relationship', to: 'categories', admin: { width: '2/3' } },
    { name: 'hsCode', type: 'text', admin: { description: 'รหัสพิกัดศุลกากร' } },
    // …
  ],
}
  • ใช้กับ field ชั้นบนสุด แต่ละ field วางได้ครั้งเดียว field ที่ไม่ได้วางจะต่อท้าย (ในแท็บแรก) ส่วน field ที่ admin: { position: 'sidebar' } ยังอยู่ในแถบข้าง
  • ถ้ามีแท็บ ทุกรายการชั้นบนสุดต้องเป็นแท็บ แท็บแสดงจำนวน field ที่ผิดในแท็บนั้น
  • row วาง field ข้างกัน แบ่งเท่ากันหรือตาม admin.width ('1/4'…'full') บนจอแคบจะเรียงลงมา
  • collapsible คือส่วนที่พับได้ collapsed: true เริ่มแบบพับไว้
  • admin.description ของ field เพิ่มคำอธิบายใต้ field
  • plugin SEO ย้าย field ของตัวเองไปที่แท็บ SEO (ตั้ง tab: false ถ้าอยากให้อยู่ใต้ field อื่นเหมือนเดิม)

เงื่อนไข

admin.condition แสดง field เฉพาะเมื่อ field ข้างเคียงตรงเงื่อนไข:

fields: [
  { name: 'linkType', type: 'select', options: ['internal', 'external'] },
  { name: 'page', type: 'relationship', to: 'pages', admin: { condition: { field: 'linkType', equals: 'internal' } } },
  {
    name: 'url',
    type: 'text',
    required: true,
    admin: { condition: { field: 'linkType', equals: 'external' } },
  },
]

มี equals, not_equals, in: [...], not_in: [...] และ exists: true | false (ชื่อเดียวกับใน where) รวมกันได้ด้วย and, or และ not ส่วน field คือ field ข้างเคียง (ใน group หรือแถวของ array คือ field ที่อยู่ด้วยกัน) หรือ path เข้าไปข้างใน (link.type) server ใช้เงื่อนไขเดียวกัน: field ที่ถูกซ่อน ไม่ถูกบังคับกรอก และค่าเดิมยังอยู่

การบันทึก

  • บันทึกไม่ผ่าน: ด้านบนของฟอร์มสรุปทุกช่องที่ต้องแก้ เปิดแท็บหรือส่วนของช่องแรก และย้ายโฟกัสไปที่นั่น
  • การแก้ไขที่ยังไม่บันทึกเก็บไว้ในเบราว์เซอร์ระหว่างพิมพ์ เปิดเอกสารนั้นอีกครั้งจะถามว่าจะกู้คืนไหม
  • ⌘S บันทึก ⌘⇧P เผยแพร่

แถบข้าง

กล่องของ plugin (admin.sidebar) อยู่ใต้ field ของแถบข้าง ตั้ง position: 'top' เพื่อวางไว้ใต้ส่วนเผยแพร่ เช่น ปุ่มของคำสั่งซื้อ:

admin: { sidebar: [{ tag: 'ecms-order-actions', position: 'top' }] }

หน้ารายการ

  • กลับจากหน้าเอกสารแล้วรายการเหมือนเดิม ทั้งคำค้น ตัวกรอง หน้า และตำแหน่ง scroll
  • admin.empty บอกว่า collection นี้ใช้ทำอะไรเมื่อยังไม่มีเอกสาร:
admin: {
  empty: {
    description: 'เพิ่มสินค้าแรก: ตั้งชื่อ ใส่ราคา แล้วเผยแพร่',
    link: { label: 'คู่มือร้านค้า', href: 'https://example.com/docs/shop' },
  },
}

ตัวกรอง

ตัวกรอง เปิดแผงเงื่อนไขที่เอกสารต้องตรงทุกข้อ เช่น ชื่อเรื่องมีคำที่ค้น ตัวเลขอยู่ระหว่างสองค่า วันที่ก่อน หลัง ตรงกับ หรืออยู่ระหว่างวันที่ ตัวเลือกของ select หรือเอกสารที่เชื่อมไว้ที่เป็นอันใดอันหนึ่ง หรือช่องที่ว่าง แต่ละเงื่อนไขแสดงเป็นชิป เหนือรายการ คลิกเพื่อแก้เงื่อนไข คลิก × เพื่อเอาออก

  • วันที่นับเป็นทั้งวันตาม timeZone (ดูการตั้งค่า) ไม่ว่าจะเปิดหน้าแอดมินจากที่ไหน วันนี้, 7 วันล่าสุด และ 30 วันล่าสุด ใส่วันที่ให้ตอนที่เลือก
  • เงื่อนไขอยู่ในที่อยู่ของหน้าในรูปแบบที่ REST API อ่าน ส่งลิงก์ไปแล้วคนเปิดเห็นรายการเดียวกัน: /admin/collections/posts?where[and][0][category][in]=3&where[and][1][publishedAt][gte]=… ลิงก์แบบเดิม (?status=draft, ?f_form=3) ยังใช้ได้
  • เมนูสถานะ ของฉัน และเมนูของช่อง select (ไม่เกินสามช่อง) เพิ่มเงื่อนไขของตัวเองรวมกับเงื่อนไขอื่น
  • ช่องใน array หรือ blocks, rich text และ JSON กรองไม่ได้ ถ้าไม่อยากให้กรองช่องอื่นด้วย ตั้ง admin: { filter: false } ที่ช่องนั้น
  • collection ใหญ่ ควรใส่ index (index: true) ให้ช่องที่กรองบ่อย

ปฏิทิน

บทความบนปฏิทินรายเดือน สีตามหมวดหมู่ และบทความของวันนี้พร้อมสถานะในการ์ดด้านข้างบทความบนปฏิทินรายเดือน สีตามหมวดหมู่ และบทความของวันนี้พร้อมสถานะในการ์ดด้านข้าง

collection ที่มี admin.calendar จะมีปุ่ม รายการ | ปฏิทิน เหนือหน้ารายการ:

admin: {
  calendar: { start: 'startsAt', end: 'endsAt', color: 'category' },
}
  • มีมุมมองเดือน สัปดาห์ วัน และกำหนดการ แสดงตามเขตเวลาของแอดมิน (บอกชื่อเขตไว้เหนือปฏิทิน) มุมมองและวันที่ที่ดูอยู่ เก็บในที่อยู่ของหน้า ส่งลิงก์ไปแล้วเปิดเห็นปฏิทินเดียวกัน
  • ใช้ตัวกรองและคำค้นของหน้ารายการ ถ้าช่วงที่ดูมีเกิน 500 รายการ จะแสดง 500 รายการแรกและบอกให้กรองเพิ่ม
  • คลิกเอกสารเพื่อเปิด คลิกวัน (หรือตัวเลขวันด้วยคีย์บอร์ด) เพื่อดูวันนั้นในการ์ดข้างปฏิทิน ซึ่งแสดงเอกสารเรียงตามเวลา รายการทั้งวันขึ้นก่อน พร้อมสถานะ ปุ่ม สร้างในวันนี้ ในการ์ดสร้างเอกสารที่เริ่มวันนั้น เช่นเดียวกับการเลือกช่วงเวลาในมุมมอง สัปดาห์หรือวัน (ถ้ามี drafts จะสร้างเป็นฉบับร่าง) วันที่เลือกอยู่ในที่อยู่ของหน้าด้วย (&day=)
  • เอกสารที่เริ่มเที่ยงคืนและไม่มีวันจบ (หรือจบเที่ยงคืน) แสดงเต็มวัน ถ้าไม่ได้ตั้ง end เอกสารอื่นใช้เวลาหนึ่งชั่วโมง
  • color ให้ค่าแต่ละค่าของ field select หรือ relationship มีสีของตัวเอง
  • ลากเอกสารไปวันหรือเวลาอื่นได้ หรือลากปลายเพื่อเปลี่ยนเวลาจบในมุมมองสัปดาห์และวัน (ถ้ามี end) ปุ่ม ย้ายไปวันที่… ในการ์ดของวันทำแบบเดียวกันด้วยคีย์บอร์ด โดยคงความยาวเดิม จะมีข้อความพร้อมปุ่ม เลิกทำ อยู่ 8 วินาที ถ้า server ไม่รับการบันทึก เอกสารจะกลับที่เดิมพร้อมบอกเหตุผล การย้ายบันทึกเหมือนในหน้าแก้ไข ถ้ามี drafts และ versions เอกสารที่มีฉบับร่างค้างอยู่จะย้ายในฉบับร่าง และหน้าเว็บเปลี่ยนเมื่อเผยแพร่ การย้ายต้องมีสิทธิ์แก้ไข

ปฏิทินโหลดเมื่อมีคนเปิดใช้เท่านั้น

หน้าปฏิทิน

ถ้ามี collection ที่ตั้ง admin.calendar หรือ schedule เมนูจะมี ปฏิทิน ต่อจากกลุ่มเนื้อหา แสดงเอกสารของ collection เหล่านั้นแยกสีกัน และสิ่งที่ตั้งเวลาเผยแพร่หรือเลิกเผยแพร่ไว้ (แสดงเป็นกรอบเส้นประ) ทั้งเว็บ การ์ด แสดง ข้างปฏิทินใช้ซ่อนบางอย่าง (จำไว้ในเบราว์เซอร์ของแต่ละคน) ส่วนการ์ดของวันแสดงวันที่เลือก เริ่มต้นเป็นวันนี้ การลากการตั้งเวลาเผยแพร่ (หรือ ย้ายไปวันที่…) ตั้งเวลาใหม่ตามที่ย้าย สำหรับผู้มีสิทธิ์เผยแพร่ คลิกเพื่อเปิดเอกสาร ถ้าจะสร้างเอกสารจากวันที่ ให้ใช้โหมดปฏิทินของ collection นั้น

ทุกคนที่ login เห็นหน้านี้ แต่เห็นเฉพาะสิ่งที่อ่านได้ (การตั้งเวลาเผยแพร่: เฉพาะที่แก้ไขได้) ถ้าใช้ auth.rbac ให้สิทธิ์บทบาทได้ที่ ตั้งค่า → บทบาท (calendar) ส่วน admin เห็นเสมอ

เขตเวลา

ถ้าตั้ง timeZone ใน config (ดูการตั้งค่า) หน้าแอดมินจะแสดงวันเวลาทั้งหมดตามเขตนั้น และช่องกรอกวันที่ การตั้งเวลาเผยแพร่ และตัวกรองวันที่ของหน้ารายการก็อ่านค่าที่กรอกตามเขตนั้นด้วย โพสต์ที่ตั้งไว้ 09:00 จะเป็น 09:00 สำหรับทุกคน ไม่ว่าเบราว์เซอร์จะอยู่ที่ไหน คนที่อยู่ต่างเขตจะเห็นชื่อเขตเวลาข้างช่องกรอกวันที่ ("เขตเวลา: เวลาอินโดจีน (GMT+7)") ถ้าไม่ได้ตั้ง timeZone หน้าแอดมินใช้เขตเวลาของเบราว์เซอร์แต่ละเครื่อง

ขั้นต่อไป

  • Plugins: ให้ collection และหน้าของ plugin มีกลุ่มของตัวเอง
  • Fields: field ที่ layout เหล่านี้จัดวาง
  • บทบาทและสิทธิ์: สิ่งที่แต่ละคนเห็นในเมนู