20260810172235
This commit is contained in:
1 parent
c4891538a7
commit
ed5ab3885d
35 files changed
+1314
-653
No files matched your search
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">联系人</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">合作伙伴</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">其他数据</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,8 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import FmsSelect from "@/components/FmsSelect.vue"
|
||||
import FmsAutoComplete from "@/components/FmsAutoComplete.vue"
|
||||
|
||||
// FmsSelect demo
|
||||
const localeCode = ref("")
|
||||
const localeLabel = ref("")
|
||||
const localeRecord = ref<Record<string, any> | null>(null)
|
||||
|
||||
function onLocaleChange(record: Record<string, any> | null) {
|
||||
localeRecord.value = record
|
||||
}
|
||||
|
||||
// FmsAutoComplete demo
|
||||
const autoValue = ref("")
|
||||
const autoRecord = ref<Record<string, any> | null>(null)
|
||||
|
||||
function onAutoChange(record: Record<string, any> | null) {
|
||||
autoRecord.value = record
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<div class="flex h-full flex-col items-center gap-8 p-8">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">Dashboard</h1>
|
||||
|
||||
<div class="grid w-full max-w-2xl gap-8">
|
||||
<!-- FmsSelect demo -->
|
||||
<div class="rounded-lg border p-6 space-y-3">
|
||||
<h2 class="text-sm font-semibold">FmsSelect(多语言类型)</h2>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
v-model + v-model:label 双绑定,支持远程搜索、滚动分页、清除、未匹配值回显
|
||||
</p>
|
||||
<div class="w-64">
|
||||
<FmsSelect
|
||||
v-model="localeCode"
|
||||
v-model:label="localeLabel"
|
||||
view-name="b_i18n_type"
|
||||
label-key="b_name"
|
||||
value-key="b_code"
|
||||
order-by="b_code ASC"
|
||||
:page-size="10"
|
||||
allow-clear
|
||||
placeholder="选择语言类型..."
|
||||
@change="onLocaleChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs text-muted-foreground space-y-0.5">
|
||||
<div>value(b_code):<code class="text-foreground">{{ localeCode || '-' }}</code></div>
|
||||
<div>label(b_name):<code class="text-foreground">{{ localeLabel || '-' }}</code></div>
|
||||
<div>record:<code class="text-foreground">{{ localeRecord || '-' }}</code></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FmsAutoComplete demo -->
|
||||
<div class="rounded-lg border p-6 space-y-3">
|
||||
<h2 class="text-sm font-semibold">FmsAutoComplete(语言类型搜索)</h2>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
单 v-model 绑定文本,回车/选中后拿到完整记录
|
||||
</p>
|
||||
<div class="w-64">
|
||||
<FmsAutoComplete
|
||||
v-model="autoValue"
|
||||
view-name="b_i18n_type"
|
||||
label-key="b_name"
|
||||
value-key="b_code"
|
||||
order-by="b_code ASC"
|
||||
placeholder="输入语言关键字..."
|
||||
@change="onAutoChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs text-muted-foreground space-y-0.5">
|
||||
<div>v-model:<code class="text-foreground">{{ autoValue || '-' }}</code></div>
|
||||
<div>record:<code class="text-foreground">{{ autoRecord || '-' }}</code></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,63 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
|
||||
const stats = [
|
||||
{ label: '总用户数', value: '12,345', trend: '+12.5%', up: true },
|
||||
{ label: '活跃用户', value: '8,920', trend: '+8.2%', up: true },
|
||||
{ label: '本月新增', value: '1,203', trend: '-3.1%', up: false },
|
||||
{ label: '转化率', value: '24.8%', trend: '+5.6%', up: true },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold">卡片示例</h2>
|
||||
<p class="mt-1 text-sm text-muted-foreground">统计卡片和内容卡片</p>
|
||||
</div>
|
||||
|
||||
<!-- Stat cards -->
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<Card v-for="stat in stats" :key="stat.label">
|
||||
<CardHeader class="pb-2">
|
||||
<CardDescription>{{ stat.label }}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="pb-2">
|
||||
<div class="text-2xl font-bold">{{ stat.value }}</div>
|
||||
</CardContent>
|
||||
<CardFooter class="text-xs" :class="stat.up ? 'text-emerald-600' : 'text-red-500'">
|
||||
{{ stat.trend }}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Content cards -->
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>最近活动</CardTitle>
|
||||
<CardDescription>团队最近的操作记录</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-2 text-sm text-muted-foreground">
|
||||
<p>• 张三 新增了一条客户记录</p>
|
||||
<p>• 李四 更新了产品配置</p>
|
||||
<p>• 王五 导出了本月报表</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>快捷操作</CardTitle>
|
||||
<CardDescription>常用功能入口</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="flex flex-wrap gap-2">
|
||||
<Button variant="outline">新建订单</Button>
|
||||
<Button variant="outline">导入数据</Button>
|
||||
<Button variant="outline">生成报表</Button>
|
||||
<Button variant="outline">发送通知</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,48 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
|
||||
const form = ref({ name: '', email: '', phone: '' })
|
||||
const submitted = ref(false)
|
||||
|
||||
function handleSubmit() {
|
||||
submitted.value = true
|
||||
setTimeout(() => { submitted.value = false }, 3000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold">表单示例</h2>
|
||||
<p class="mt-1 text-sm text-muted-foreground">基础的输入表单</p>
|
||||
</div>
|
||||
|
||||
<div class="max-w-lg rounded-lg border bg-card p-6">
|
||||
<form class="space-y-4" @submit.prevent="handleSubmit">
|
||||
<div class="space-y-2">
|
||||
<Label for="name">姓名</Label>
|
||||
<Input id="name" v-model="form.name" placeholder="请输入姓名" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="email">邮箱</Label>
|
||||
<Input id="email" v-model="form.email" type="email" placeholder="请输入邮箱地址" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="phone">手机号</Label>
|
||||
<Input id="phone" v-model="form.phone" placeholder="请输入手机号" />
|
||||
</div>
|
||||
<div class="flex items-center gap-3 pt-2">
|
||||
<Button type="submit">提交</Button>
|
||||
<Button type="button" variant="outline">重置</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div v-if="submitted" class="mt-4 rounded-md bg-emerald-50 px-4 py-3 text-sm text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300">
|
||||
表单提交成功!提交的数据:{{ form }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,71 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
|
||||
const settings = ref({
|
||||
notifications: true,
|
||||
autoSave: true,
|
||||
compactMode: false,
|
||||
analytics: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold">设置示例</h2>
|
||||
<p class="mt-1 text-sm text-muted-foreground">系统设置与偏好</p>
|
||||
</div>
|
||||
|
||||
<Card class="max-w-2xl">
|
||||
<CardHeader>
|
||||
<CardTitle>通知设置</CardTitle>
|
||||
<CardDescription>管理系统通知偏好</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>消息通知</Label>
|
||||
<p class="text-sm text-muted-foreground">接收系统消息和提醒</p>
|
||||
</div>
|
||||
<Switch v-model="settings.notifications" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>自动保存</Label>
|
||||
<p class="text-sm text-muted-foreground">编辑时自动保存内容</p>
|
||||
</div>
|
||||
<Switch v-model="settings.autoSave" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>紧凑模式</Label>
|
||||
<p class="text-sm text-muted-foreground">缩小间距,显示更多内容</p>
|
||||
</div>
|
||||
<Switch v-model="settings.compactMode" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>数据统计</Label>
|
||||
<p class="text-sm text-muted-foreground">允许收集使用数据</p>
|
||||
</div>
|
||||
<Switch v-model="settings.analytics" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card class="max-w-2xl">
|
||||
<CardHeader>
|
||||
<CardTitle>危险操作</CardTitle>
|
||||
<CardDescription>不可恢复的操作,请谨慎</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="flex gap-3">
|
||||
<Button variant="outline" class="text-amber-600 border-amber-600 hover:bg-amber-50">导出所有数据</Button>
|
||||
<Button variant="outline" class="text-red-600 border-red-600 hover:bg-red-50">删除账号</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
|
||||
const data = [
|
||||
{ id: 1, name: '张三', department: '技术部', role: '前端开发', status: '在职' },
|
||||
{ id: 2, name: '李四', department: '产品部', role: '产品经理', status: '在职' },
|
||||
{ id: 3, name: '王五', department: '设计部', role: 'UI 设计', status: '休假' },
|
||||
{ id: 4, name: '赵六', department: '技术部', role: '后端开发', status: '在职' },
|
||||
{ id: 5, name: '钱七', department: '市场部', role: '市场运营', status: '离职' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold">表格示例</h2>
|
||||
<p class="mt-1 text-sm text-muted-foreground">一个简单的数据表格页面</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Input placeholder="搜索..." class="w-48" />
|
||||
<Button variant="outline">导出</Button>
|
||||
<Button>新增</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border bg-card">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="border-b bg-muted/50">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left font-medium">姓名</th>
|
||||
<th class="px-4 py-3 text-left font-medium">部门</th>
|
||||
<th class="px-4 py-3 text-left font-medium">角色</th>
|
||||
<th class="px-4 py-3 text-left font-medium">状态</th>
|
||||
<th class="px-4 py-3 text-right font-medium">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in data" :key="row.id" class="border-b last:border-b-0 hover:bg-muted/50 transition-colors">
|
||||
<td class="px-4 py-3">{{ row.name }}</td>
|
||||
<td class="px-4 py-3">{{ row.department }}</td>
|
||||
<td class="px-4 py-3 text-muted-foreground">{{ row.role }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="inline-flex items-center gap-1.5 text-xs font-medium" :class="row.status === '在职' ? 'text-emerald-600' : row.status === '休假' ? 'text-amber-600' : 'text-muted-foreground'">
|
||||
<span class="size-1.5 rounded-full" :class="row.status === '在职' ? 'bg-emerald-500' : row.status === '休假' ? 'bg-amber-500' : 'bg-muted-foreground'" />
|
||||
{{ row.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<Button variant="ghost" size="xs">编辑</Button>
|
||||
<Button variant="ghost" size="xs">查看</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between text-sm text-muted-foreground">
|
||||
<span>共 {{ data.length }} 条记录</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button variant="outline" size="sm" disabled>上一页</Button>
|
||||
<span>第 1 页</span>
|
||||
<Button variant="outline" size="sm">下一页</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
+28
-11
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref } from "vue"
|
||||
import { computed, onMounted, ref } from "vue"
|
||||
import { useRoute, useRouter } from "vue-router"
|
||||
import { useForm } from "vee-validate"
|
||||
import { toast } from "vue-sonner"
|
||||
import { loginApi } from "@/services/api"
|
||||
import { useAuthStore } from "@/stores/auth"
|
||||
import { Building2, GalleryVerticalEnd, KeyRound, User } from "@lucide/vue"
|
||||
import { Building2, GalleryVerticalEnd, KeyRound, LoaderCircle, User } from "@lucide/vue"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import {
|
||||
@@ -36,22 +38,32 @@ const formSchema = toTypedSchema(
|
||||
}),
|
||||
)
|
||||
|
||||
const form = useForm({
|
||||
validationSchema: formSchema,
|
||||
})
|
||||
|
||||
const submitting = ref(false)
|
||||
const orgIdInput = ref<InstanceType<typeof Input> | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
(document.getElementById("orgId") || document.getElementById("password"))?.focus()
|
||||
form.setValues({
|
||||
orgId: authStore.orgId || '',
|
||||
userId: authStore.user?.account || '',
|
||||
password: '',
|
||||
})
|
||||
orgIdInput.value?.$el?.focus()
|
||||
})
|
||||
|
||||
async function onSubmit(values: Record<string, unknown>) {
|
||||
submitting.value = true
|
||||
try {
|
||||
const res = await loginApi(values.orgId as string, values.userId as string, (values.password as string) ?? "")
|
||||
const { orgId, userId, password } = values as { orgId: string; userId: string; password?: string }
|
||||
const res = await loginApi(orgId, userId, password ?? "")
|
||||
authStore.setSession(
|
||||
{ token: res.data.token, user: { ...res.data.user, account: res.data.user.id } },
|
||||
res.data.orgid ?? (values.orgId as string),
|
||||
{ token: res.data.token, user: { ...res.data.user, account: userId } },
|
||||
res.data.orgid ?? orgId,
|
||||
)
|
||||
toast.success('登录成功')
|
||||
router.replace(String(route.query.redirect || "/dashboard"))
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) alert(e.message)
|
||||
@@ -59,12 +71,16 @@ async function onSubmit(values: Record<string, unknown>) {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function login() {
|
||||
form.handleSubmit(onSubmit)()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex min-h-svh flex-col items-center justify-center bg-background p-6 md:p-10">
|
||||
<div class="w-full max-w-sm">
|
||||
<Form class="flex flex-col gap-6" :validation-schema="formSchema" @submit="onSubmit">
|
||||
<Form class="flex flex-col gap-6" :form="form">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col items-center gap-2 text-center">
|
||||
<a href="#" class="flex flex-col items-center gap-2 font-medium">
|
||||
@@ -89,7 +105,7 @@ async function onSubmit(values: Record<string, unknown>) {
|
||||
<FormControl>
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-3 flex items-center text-muted-foreground"><Building2 class="size-4" /></span>
|
||||
<Input id="orgId" v-bind="componentField" placeholder="请输入机构码" class="pl-9" :disabled="submitting" />
|
||||
<Input id="orgId" ref="orgIdInput" v-bind="componentField" placeholder="请输入机构码" class="pl-9" :disabled="submitting" />
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -124,8 +140,9 @@ async function onSubmit(values: Record<string, unknown>) {
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<Button type="submit" :disabled="submitting" class="w-full cursor-pointer">
|
||||
{{ submitting ? "登录中..." : "登录" }}
|
||||
<Button class="w-full cursor-pointer" :disabled="submitting" @click="login">
|
||||
<LoaderCircle v-if="submitting" class="size-4 animate-spin" />
|
||||
登录
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">多语言类型</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">登录日志</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">模块管理</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<h1 class="text-2xl font-bold text-muted-foreground">用户管理</h1>
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user