style(web): lint --fix sweep + disable no-undef for ts/vue (DOM globals)

This commit is contained in:
2026-05-01 10:53:02 +08:00
parent ba39fac142
commit 81718e53f9
9 changed files with 362 additions and 81 deletions
+3
View File
@@ -35,6 +35,9 @@ export default [
files: ['**/*.{ts,tsx,vue}'], files: ['**/*.{ts,tsx,vue}'],
rules: { rules: {
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',
// TypeScript/vue-tsc 已经做名字解析;no-undef 在 .vue 内对 DOM 全局
// (DragEvent / HTMLElement 等) 误报,关掉走 TS 校验。
'no-undef': 'off',
'@typescript-eslint/no-unused-vars': [ '@typescript-eslint/no-unused-vars': [
'error', 'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
+3 -1
View File
@@ -1,7 +1,9 @@
<template> <template>
<AppShell> <AppShell>
<div class="space-y-4"> <div class="space-y-4">
<h2 class="text-xl font-semibold">欢迎{{ auth.user?.display_name }}</h2> <h2 class="text-xl font-semibold">
欢迎{{ auth.user?.display_name }}
</h2>
<p>这是占位 dashboard后续 plan 会接入项目/工作区/Issue</p> <p>这是占位 dashboard后续 plan 会接入项目/工作区/Issue</p>
</div> </div>
</AppShell> </AppShell>
+24 -5
View File
@@ -1,19 +1,38 @@
<template> <template>
<div class="min-h-screen flex items-center justify-center p-4"> <div class="min-h-screen flex items-center justify-center p-4">
<div class="w-full max-w-sm space-y-4"> <div class="w-full max-w-sm space-y-4">
<h1 class="text-2xl font-semibold">登录</h1> <h1 class="text-2xl font-semibold">
登录
</h1>
<NForm @submit.prevent="onSubmit"> <NForm @submit.prevent="onSubmit">
<NFormItem label="邮箱"> <NFormItem label="邮箱">
<NInput v-model:value="email" placeholder="admin@local" /> <NInput
v-model:value="email"
placeholder="admin@local"
/>
</NFormItem> </NFormItem>
<NFormItem label="密码"> <NFormItem label="密码">
<NInput v-model:value="password" type="password" show-password-on="click" /> <NInput
v-model:value="password"
type="password"
show-password-on="click"
/>
</NFormItem> </NFormItem>
<NButton type="primary" attr-type="submit" :loading="loading" block> <NButton
type="primary"
attr-type="submit"
:loading="loading"
block
>
登录 登录
</NButton> </NButton>
</NForm> </NForm>
<p v-if="error" class="text-red-500 text-sm">{{ error }}</p> <p
v-if="error"
class="text-red-500 text-sm"
>
{{ error }}
</p>
</div> </div>
</div> </div>
</template> </template>
+38 -8
View File
@@ -1,13 +1,21 @@
<template> <template>
<AppShell> <AppShell>
<NSpin :show="loading"> <NSpin :show="loading">
<div v-if="issue" class="space-y-6"> <div
v-if="issue"
class="space-y-6"
>
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="text-gray-400 font-mono text-sm">#{{ issue.number }}</span> <span class="text-gray-400 font-mono text-sm">#{{ issue.number }}</span>
<h1 class="text-2xl font-semibold">{{ issue.title }}</h1> <h1 class="text-2xl font-semibold">
<NTag :type="issue.status === 'open' ? 'success' : 'default'" size="small"> {{ issue.title }}
</h1>
<NTag
:type="issue.status === 'open' ? 'success' : 'default'"
size="small"
>
{{ issue.status === 'open' ? '开放' : '关闭' }} {{ issue.status === 'open' ? '开放' : '关闭' }}
</NTag> </NTag>
</div> </div>
@@ -30,7 +38,12 @@
> >
重新开放 重新开放
</NButton> </NButton>
<NButton size="small" @click="router.back()">返回</NButton> <NButton
size="small"
@click="router.back()"
>
返回
</NButton>
</div> </div>
</div> </div>
@@ -52,7 +65,13 @@
{{ issue.assignee_id ? `已指派:${issue.assignee_id}` : '暂未指派' }} {{ issue.assignee_id ? `已指派:${issue.assignee_id}` : '暂未指派' }}
</span> </span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<NButton size="small" :loading="assigning" @click="onAssignToMe">指派给我</NButton> <NButton
size="small"
:loading="assigning"
@click="onAssignToMe"
>
指派给我
</NButton>
<NButton <NButton
v-if="issue.assignee_id" v-if="issue.assignee_id"
size="small" size="small"
@@ -67,14 +86,25 @@
<!-- Description card --> <!-- Description card -->
<NCard title="描述"> <NCard title="描述">
<p v-if="issue.description" class="text-gray-700 whitespace-pre-wrap"> <p
v-if="issue.description"
class="text-gray-700 whitespace-pre-wrap"
>
{{ issue.description }} {{ issue.description }}
</p> </p>
<p v-else class="text-gray-400 text-sm">暂无描述</p> <p
v-else
class="text-gray-400 text-sm"
>
暂无描述
</p>
</NCard> </NCard>
</div> </div>
<div v-else-if="!loading" class="text-gray-400 text-sm py-8 text-center"> <div
v-else-if="!loading"
class="text-gray-400 text-sm py-8 text-center"
>
Issue 不存在或无权访问 Issue 不存在或无权访问
</div> </div>
</NSpin> </NSpin>
+45 -9
View File
@@ -4,8 +4,15 @@
<div class="space-y-4"> <div class="space-y-4">
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h1 class="text-2xl font-semibold">Issue 列表</h1> <h1 class="text-2xl font-semibold">
<NButton type="primary" @click="showCreate = true">新建 Issue</NButton> Issue 列表
</h1>
<NButton
type="primary"
@click="showCreate = true"
>
新建 Issue
</NButton>
</div> </div>
<!-- Filter chips --> <!-- Filter chips -->
@@ -44,20 +51,36 @@
striped striped
/> />
<div v-if="!loading && items.length === 0" class="text-gray-400 text-sm py-8 text-center"> <div
v-if="!loading && items.length === 0"
class="text-gray-400 text-sm py-8 text-center"
>
暂无 Issue 暂无 Issue
</div> </div>
</div> </div>
</NSpin> </NSpin>
<!-- Create modal --> <!-- Create modal -->
<NModal v-model:show="showCreate" preset="card" title="新建 Issue" class="w-full max-w-md"> <NModal
v-model:show="showCreate"
preset="card"
title="新建 Issue"
class="w-full max-w-md"
>
<NForm @submit.prevent="onSubmit"> <NForm @submit.prevent="onSubmit">
<NFormItem label="标题"> <NFormItem label="标题">
<NInput v-model:value="form.title" placeholder="Issue 标题" /> <NInput
v-model:value="form.title"
placeholder="Issue 标题"
/>
</NFormItem> </NFormItem>
<NFormItem label="描述"> <NFormItem label="描述">
<NInput v-model:value="form.description" type="textarea" :rows="4" placeholder="(可选)" /> <NInput
v-model:value="form.description"
type="textarea"
:rows="4"
placeholder="(可选)"
/>
</NFormItem> </NFormItem>
<NFormItem label="挂载到 Requirement(可选)"> <NFormItem label="挂载到 Requirement(可选)">
<NSelect <NSelect
@@ -68,11 +91,24 @@
/> />
</NFormItem> </NFormItem>
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<NButton @click="showCreate = false">取消</NButton> <NButton @click="showCreate = false">
<NButton type="primary" attr-type="submit" :loading="creating">创建</NButton> 取消
</NButton>
<NButton
type="primary"
attr-type="submit"
:loading="creating"
>
创建
</NButton>
</div> </div>
</NForm> </NForm>
<p v-if="createError" class="text-red-500 text-sm mt-2">{{ createError }}</p> <p
v-if="createError"
class="text-red-500 text-sm mt-2"
>
{{ createError }}
</p>
</NModal> </NModal>
</AppShell> </AppShell>
</template> </template>
+41 -9
View File
@@ -1,32 +1,64 @@
<template> <template>
<AppShell> <AppShell>
<NSpin :show="store.loading"> <NSpin :show="store.loading">
<div v-if="store.current" class="space-y-6"> <div
v-if="store.current"
class="space-y-6"
>
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="space-y-1"> <div class="space-y-1">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<h1 class="text-2xl font-semibold">{{ store.current.name }}</h1> <h1 class="text-2xl font-semibold">
<NTag :type="store.current.visibility === 'private' ? 'warning' : 'info'" size="small"> {{ store.current.name }}
</h1>
<NTag
:type="store.current.visibility === 'private' ? 'warning' : 'info'"
size="small"
>
{{ store.current.visibility === 'private' ? '私有' : '内部' }} {{ store.current.visibility === 'private' ? '私有' : '内部' }}
</NTag> </NTag>
<NTag v-if="store.current.archived_at" size="small">已归档</NTag> <NTag
v-if="store.current.archived_at"
size="small"
>
已归档
</NTag>
</div> </div>
<p class="text-gray-500 text-sm font-mono">{{ store.current.slug }}</p> <p class="text-gray-500 text-sm font-mono">
{{ store.current.slug }}
</p>
</div> </div>
</div> </div>
<!-- Description --> <!-- Description -->
<p v-if="store.current.description" class="text-gray-600">{{ store.current.description }}</p> <p
v-if="store.current.description"
class="text-gray-600"
>
{{ store.current.description }}
</p>
<!-- Nav buttons --> <!-- Nav buttons -->
<div class="flex gap-3"> <div class="flex gap-3">
<NButton type="primary" @click="goRequirements">需求列表</NButton> <NButton
<NButton @click="goIssues">Issue 列表</NButton> type="primary"
@click="goRequirements"
>
需求列表
</NButton>
<NButton @click="goIssues">
Issue 列表
</NButton>
</div> </div>
</div> </div>
<div v-else-if="!store.loading" class="text-gray-400 text-sm">项目不存在或无权访问</div> <div
v-else-if="!store.loading"
class="text-gray-400 text-sm"
>
项目不存在或无权访问
</div>
</NSpin> </NSpin>
</AppShell> </AppShell>
</template> </template>
+87 -21
View File
@@ -4,18 +4,31 @@
<div class="space-y-6"> <div class="space-y-6">
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h1 class="text-2xl font-semibold">项目</h1> <h1 class="text-2xl font-semibold">
项目
</h1>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-sm text-gray-500">显示已归档</span> <span class="text-sm text-gray-500">显示已归档</span>
<NSwitch v-model:value="store.includeArchived" @update:value="store.refresh()" /> <NSwitch
v-model:value="store.includeArchived"
@update:value="store.refresh()"
/>
</div> </div>
<NButton type="primary" @click="showCreate = true">新建项目</NButton> <NButton
type="primary"
@click="showCreate = true"
>
新建项目
</NButton>
</div> </div>
</div> </div>
<!-- Active projects grid --> <!-- Active projects grid -->
<div v-if="store.activeItems.length > 0" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> <div
v-if="store.activeItems.length > 0"
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"
>
<NCard <NCard
v-for="p in store.activeItems" v-for="p in store.activeItems"
:key="p.id" :key="p.id"
@@ -25,20 +38,34 @@
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="font-medium">{{ p.name }}</span> <span class="font-medium">{{ p.name }}</span>
<NTag :type="p.visibility === 'private' ? 'warning' : 'info'" size="small"> <NTag
:type="p.visibility === 'private' ? 'warning' : 'info'"
size="small"
>
{{ p.visibility === 'private' ? '私有' : '内部' }} {{ p.visibility === 'private' ? '私有' : '内部' }}
</NTag> </NTag>
</div> </div>
<p class="text-sm text-gray-500 line-clamp-2">{{ p.description || '暂无描述' }}</p> <p class="text-sm text-gray-500 line-clamp-2">
<p class="text-xs text-gray-400 font-mono">{{ p.slug }}</p> {{ p.description || '暂无描述' }}
</p>
<p class="text-xs text-gray-400 font-mono">
{{ p.slug }}
</p>
</div> </div>
</NCard> </NCard>
</div> </div>
<div v-else-if="!store.loading" class="text-gray-400 text-sm">暂无活跃项目</div> <div
v-else-if="!store.loading"
class="text-gray-400 text-sm"
>
暂无活跃项目
</div>
<!-- Archived projects --> <!-- Archived projects -->
<template v-if="store.includeArchived && store.archivedItems.length > 0"> <template v-if="store.includeArchived && store.archivedItems.length > 0">
<h2 class="text-base font-medium text-gray-500">已归档</h2> <h2 class="text-base font-medium text-gray-500">
已归档
</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<NCard <NCard
v-for="p in store.archivedItems" v-for="p in store.archivedItems"
@@ -49,10 +76,16 @@
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="font-medium">{{ p.name }}</span> <span class="font-medium">{{ p.name }}</span>
<NTag size="small">已归档</NTag> <NTag size="small">
已归档
</NTag>
</div> </div>
<p class="text-sm text-gray-500 line-clamp-2">{{ p.description || '暂无描述' }}</p> <p class="text-sm text-gray-500 line-clamp-2">
<p class="text-xs text-gray-400 font-mono">{{ p.slug }}</p> {{ p.description || '暂无描述' }}
</p>
<p class="text-xs text-gray-400 font-mono">
{{ p.slug }}
</p>
</div> </div>
</NCard> </NCard>
</div> </div>
@@ -61,29 +94,62 @@
</NSpin> </NSpin>
<!-- Create project modal --> <!-- Create project modal -->
<NModal v-model:show="showCreate" preset="card" title="新建项目" class="w-full max-w-md"> <NModal
v-model:show="showCreate"
preset="card"
title="新建项目"
class="w-full max-w-md"
>
<NForm @submit.prevent="onSubmit"> <NForm @submit.prevent="onSubmit">
<NFormItem label="Slug(URL 标识)"> <NFormItem label="Slug(URL 标识)">
<NInput v-model:value="form.slug" placeholder="my-project" /> <NInput
v-model:value="form.slug"
placeholder="my-project"
/>
</NFormItem> </NFormItem>
<NFormItem label="名称"> <NFormItem label="名称">
<NInput v-model:value="form.name" placeholder="My Project" /> <NInput
v-model:value="form.name"
placeholder="My Project"
/>
</NFormItem> </NFormItem>
<NFormItem label="描述"> <NFormItem label="描述">
<NInput v-model:value="form.description" type="textarea" :rows="3" placeholder="(可选)" /> <NInput
v-model:value="form.description"
type="textarea"
:rows="3"
placeholder="(可选)"
/>
</NFormItem> </NFormItem>
<NFormItem label="可见性"> <NFormItem label="可见性">
<NRadioGroup v-model:value="form.visibility"> <NRadioGroup v-model:value="form.visibility">
<NRadio value="private">私有</NRadio> <NRadio value="private">
<NRadio value="internal">内部</NRadio> 私有
</NRadio>
<NRadio value="internal">
内部
</NRadio>
</NRadioGroup> </NRadioGroup>
</NFormItem> </NFormItem>
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<NButton @click="showCreate = false">取消</NButton> <NButton @click="showCreate = false">
<NButton type="primary" attr-type="submit" :loading="creating">创建</NButton> 取消
</NButton>
<NButton
type="primary"
attr-type="submit"
:loading="creating"
>
创建
</NButton>
</div> </div>
</NForm> </NForm>
<p v-if="createError" class="text-red-500 text-sm mt-2">{{ createError }}</p> <p
v-if="createError"
class="text-red-500 text-sm mt-2"
>
{{ createError }}
</p>
</NModal> </NModal>
</AppShell> </AppShell>
</template> </template>
@@ -1,13 +1,21 @@
<template> <template>
<AppShell> <AppShell>
<NSpin :show="loading"> <NSpin :show="loading">
<div v-if="data" class="space-y-6"> <div
v-if="data"
class="space-y-6"
>
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="text-gray-400 font-mono text-sm">#{{ data.requirement.number }}</span> <span class="text-gray-400 font-mono text-sm">#{{ data.requirement.number }}</span>
<h1 class="text-2xl font-semibold">{{ data.requirement.title }}</h1> <h1 class="text-2xl font-semibold">
<NTag :type="data.requirement.status === 'open' ? 'success' : 'default'" size="small"> {{ data.requirement.title }}
</h1>
<NTag
:type="data.requirement.status === 'open' ? 'success' : 'default'"
size="small"
>
{{ data.requirement.status === 'open' ? '开放' : '关闭' }} {{ data.requirement.status === 'open' ? '开放' : '关闭' }}
</NTag> </NTag>
</div> </div>
@@ -30,13 +38,21 @@
> >
重新开放 重新开放
</NButton> </NButton>
<NButton size="small" @click="router.back()">返回</NButton> <NButton
size="small"
@click="router.back()"
>
返回
</NButton>
</div> </div>
</div> </div>
<!-- Phase flow bar --> <!-- Phase flow bar -->
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<template v-for="(phase, idx) in PHASES" :key="phase"> <template
v-for="(phase, idx) in PHASES"
:key="phase"
>
<NButton <NButton
:type="phase === data.requirement.phase ? 'primary' : 'default'" :type="phase === data.requirement.phase ? 'primary' : 'default'"
:secondary="phase !== data.requirement.phase" :secondary="phase !== data.requirement.phase"
@@ -46,22 +62,39 @@
> >
{{ PHASE_LABELS[phase] }} {{ PHASE_LABELS[phase] }}
</NButton> </NButton>
<span v-if="idx < PHASES.length - 1" class="text-gray-300 text-xs"></span> <span
v-if="idx < PHASES.length - 1"
class="text-gray-300 text-xs"
></span>
</template> </template>
</div> </div>
<!-- Description --> <!-- Description -->
<NCard title="描述"> <NCard title="描述">
<p v-if="data.requirement.description" class="text-gray-700 whitespace-pre-wrap"> <p
v-if="data.requirement.description"
class="text-gray-700 whitespace-pre-wrap"
>
{{ data.requirement.description }} {{ data.requirement.description }}
</p> </p>
<p v-else class="text-gray-400 text-sm">暂无描述</p> <p
v-else
class="text-gray-400 text-sm"
>
暂无描述
</p>
</NCard> </NCard>
<!-- Linked Issues --> <!-- Linked Issues -->
<NCard title="关联 Issue"> <NCard title="关联 Issue">
<NList v-if="data.issues.length > 0" :show-divider="false"> <NList
<NListItem v-for="issue in data.issues" :key="issue.id"> v-if="data.issues.length > 0"
:show-divider="false"
>
<NListItem
v-for="issue in data.issues"
:key="issue.id"
>
<NThing> <NThing>
<template #header> <template #header>
<span class="font-mono text-xs text-gray-400 mr-2">#{{ issue.number }}</span> <span class="font-mono text-xs text-gray-400 mr-2">#{{ issue.number }}</span>
@@ -78,11 +111,17 @@
</NThing> </NThing>
</NListItem> </NListItem>
</NList> </NList>
<NEmpty v-else description="暂无关联 Issue" /> <NEmpty
v-else
description="暂无关联 Issue"
/>
</NCard> </NCard>
</div> </div>
<div v-else-if="!loading" class="text-gray-400 text-sm py-8 text-center"> <div
v-else-if="!loading"
class="text-gray-400 text-sm py-8 text-center"
>
需求不存在或无权访问 需求不存在或无权访问
</div> </div>
</NSpin> </NSpin>
+70 -16
View File
@@ -4,18 +4,35 @@
<div class="space-y-4"> <div class="space-y-4">
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h1 class="text-2xl font-semibold">需求列表</h1> <h1 class="text-2xl font-semibold">
需求列表
</h1>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<NRadioGroup v-model:value="viewMode" size="small"> <NRadioGroup
<NRadioButton value="kanban">看板</NRadioButton> v-model:value="viewMode"
<NRadioButton value="table">表格</NRadioButton> size="small"
>
<NRadioButton value="kanban">
看板
</NRadioButton>
<NRadioButton value="table">
表格
</NRadioButton>
</NRadioGroup> </NRadioGroup>
<NButton type="primary" @click="showCreate = true">新建需求</NButton> <NButton
type="primary"
@click="showCreate = true"
>
新建需求
</NButton>
</div> </div>
</div> </div>
<!-- Kanban view --> <!-- Kanban view -->
<div v-if="viewMode === 'kanban'" class="flex gap-3 overflow-x-auto pb-2"> <div
v-if="viewMode === 'kanban'"
class="flex gap-3 overflow-x-auto pb-2"
>
<div <div
v-for="phase in PHASES" v-for="phase in PHASES"
:key="phase" :key="phase"
@@ -39,14 +56,22 @@
<div class="space-y-1"> <div class="space-y-1">
<div class="flex items-center justify-between gap-1"> <div class="flex items-center justify-between gap-1">
<span class="text-xs text-gray-400 font-mono">#{{ r.number }}</span> <span class="text-xs text-gray-400 font-mono">#{{ r.number }}</span>
<NTag :type="r.status === 'open' ? 'success' : 'default'" size="tiny"> <NTag
:type="r.status === 'open' ? 'success' : 'default'"
size="tiny"
>
{{ r.status === 'open' ? '开放' : '关闭' }} {{ r.status === 'open' ? '开放' : '关闭' }}
</NTag> </NTag>
</div> </div>
<p class="text-sm font-medium leading-snug">{{ r.title }}</p> <p class="text-sm font-medium leading-snug">
{{ r.title }}
</p>
</div> </div>
</NCard> </NCard>
<div v-if="columnItems(phase).length === 0" class="text-xs text-gray-300 text-center py-4"> <div
v-if="columnItems(phase).length === 0"
class="text-xs text-gray-300 text-center py-4"
>
拖放到此处 拖放到此处
</div> </div>
</div> </div>
@@ -62,27 +87,56 @@
striped striped
/> />
<div v-if="!store.loading && store.items.length === 0" class="text-gray-400 text-sm py-8 text-center"> <div
v-if="!store.loading && store.items.length === 0"
class="text-gray-400 text-sm py-8 text-center"
>
暂无需求点击新建需求创建第一条 暂无需求点击新建需求创建第一条
</div> </div>
</div> </div>
</NSpin> </NSpin>
<!-- Create modal --> <!-- Create modal -->
<NModal v-model:show="showCreate" preset="card" title="新建需求" class="w-full max-w-md"> <NModal
v-model:show="showCreate"
preset="card"
title="新建需求"
class="w-full max-w-md"
>
<NForm @submit.prevent="onSubmit"> <NForm @submit.prevent="onSubmit">
<NFormItem label="标题"> <NFormItem label="标题">
<NInput v-model:value="form.title" placeholder="需求标题" /> <NInput
v-model:value="form.title"
placeholder="需求标题"
/>
</NFormItem> </NFormItem>
<NFormItem label="描述"> <NFormItem label="描述">
<NInput v-model:value="form.description" type="textarea" :rows="4" placeholder="(可选)" /> <NInput
v-model:value="form.description"
type="textarea"
:rows="4"
placeholder="(可选)"
/>
</NFormItem> </NFormItem>
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<NButton @click="showCreate = false">取消</NButton> <NButton @click="showCreate = false">
<NButton type="primary" attr-type="submit" :loading="creating">创建</NButton> 取消
</NButton>
<NButton
type="primary"
attr-type="submit"
:loading="creating"
>
创建
</NButton>
</div> </div>
</NForm> </NForm>
<p v-if="createError" class="text-red-500 text-sm mt-2">{{ createError }}</p> <p
v-if="createError"
class="text-red-500 text-sm mt-2"
>
{{ createError }}
</p>
</NModal> </NModal>
</AppShell> </AppShell>
</template> </template>