2
0

4 Commits 7f2bec6ea9 ... 37a8db0055

Autor SHA1 Mensagem Data
  chen 37a8db0055 feature:分类管理接口对接 1 ano atrás
  依力 f3d5e881af Merge branch 'feature/25-login' of 1-bright/admin into main 1 ano atrás
  chen 351511a2db feat(/pages/common):完成登陆页文案、组件样式修改、登陆接口对接 1 ano atrás
  依力 4eeba56696 Merge branch 'feature/BRT-23-classify' of 1-bright/admin into main 1 ano atrás

+ 1 - 1
.env

@@ -1,3 +1,3 @@
 VITE_ENABLE_MOCK=false
 VITE_API_PREFIX=/api
-VITE_PROXY_ENDPOINT=http://localhost:8000
+VITE_PROXY_ENDPOINT=https://bright.yilibili.com/api

+ 2 - 7
components.d.ts

@@ -8,7 +8,6 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     Copyright: typeof import('./src/components/Copyright.vue')['default']
-    Dialog: typeof import('./src/components/Dialog.vue')['default']
     RegularPage: typeof import('./src/components/RegularPage.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
@@ -16,8 +15,6 @@ declare module 'vue' {
     TAvatar: typeof import('tdesign-vue-next')['Avatar']
     TButton: typeof import('tdesign-vue-next')['Button']
     TCard: typeof import('tdesign-vue-next')['Card']
-    TCheckbox: typeof import('tdesign-vue-next')['Checkbox']
-    TCol: typeof import('tdesign-vue-next')['Col']
     TDialog: typeof import('tdesign-vue-next')['Dialog']
     TDropdown: typeof import('tdesign-vue-next')['Dropdown']
     TForm: typeof import('tdesign-vue-next')['Form']
@@ -25,13 +22,11 @@ declare module 'vue' {
     THeader: typeof import('tdesign-vue-next')['Header']
     TIcon: typeof import('tdesign-vue-next')['Icon']
     TInput: typeof import('tdesign-vue-next')['Input']
+    TInputNumber: typeof import('tdesign-vue-next')['InputNumber']
     TLayout: typeof import('tdesign-vue-next')['Layout']
-    TLink: typeof import('tdesign-vue-next')['Link']
     TMenu: typeof import('tdesign-vue-next')['Menu']
     TMenuItem: typeof import('tdesign-vue-next')['MenuItem']
-    TPagination: typeof import('tdesign-vue-next')['Pagination']
-    TRow: typeof import('tdesign-vue-next')['Row']
-    TSelect: typeof import('tdesign-vue-next')['Select']
+    TPopconfirm: typeof import('tdesign-vue-next')['Popconfirm']
     TSpace: typeof import('tdesign-vue-next')['Space']
     TTable: typeof import('tdesign-vue-next')['Table']
     TTag: typeof import('tdesign-vue-next')['Tag']

+ 1 - 1
index.html

@@ -4,7 +4,7 @@
     <meta charset="UTF-8">
     <link rel="icon" href="/favicon.ico">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Yili Admin</title>
+    <title>布兰德后台管理</title>
   </head>
   <body>
     <div id="app"></div>

+ 9 - 0
pnpm-lock.yaml

@@ -557,46 +557,55 @@ packages:
     resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm-musleabihf@4.22.4':
     resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==}
     cpu: [arm]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-arm64-gnu@4.22.4':
     resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm64-musl@4.22.4':
     resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-powerpc64le-gnu@4.22.4':
     resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==}
     cpu: [ppc64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-gnu@4.22.4':
     resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-s390x-gnu@4.22.4':
     resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-gnu@4.22.4':
     resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-musl@4.22.4':
     resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-win32-arm64-msvc@4.22.4':
     resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==}

+ 23 - 0
src/api/category.ts

@@ -0,0 +1,23 @@
+import httpClient from '@/api/httpClient'
+import type { PageResult } from '@/model/base'
+import type { Category,CategorySearchFilter,CategoryDeleteRequest } from '@/model/category'
+
+export const searchCategoryRequest = (categoryFilterParam:CategorySearchFilter ) => {
+  return httpClient.get<PageResult<Category>>('/categories',categoryFilterParam)
+}
+
+export const createCategoryRequest = (category: Pick<Category,'name'|'code'|'order'>) => {
+  return httpClient.post<Category>('/categories',category)
+}
+
+export const deleteCategoryRequest = (categoryDeleteParam:CategoryDeleteRequest) => {
+  return httpClient.delete(`/categories`,categoryDeleteParam)
+}
+
+export const searchCategoryByIdRequest = (id: string) => {
+  return httpClient.get<Category>(`/categories/${id}`)
+}
+
+export const updateCategoryByIdRequest = (id: string,category: Pick<Category,'name'|'code'|'order'>) => {
+  return httpClient.put<Category>(`/categories/${id}`,category)
+}

+ 13 - 9
src/api/httpClient.ts

@@ -1,5 +1,5 @@
-import axios, { type AxiosInstance } from 'axios'
-import type { ErrorResponse } from '@/model/base'
+import axios, { type AxiosInstance, } from 'axios'
+import type { ErrorResponse,ErrorResponseList } from '@/model/base'
 import { MessagePlugin } from 'tdesign-vue-next'
 import { useAppStore } from '@/stores/app'
 
@@ -9,18 +9,22 @@ const client: AxiosInstance = axios.create({
 })
 
 const handleError = (errorResponse: ErrorResponse) => {
+  
   MessagePlugin.error(errorResponse.message)
 }
 client.interceptors.response.use(
   (response) => {
-    if (response.data.code) {
-      handleError(response.data)
-      return Promise.reject(response.data)
-    }
     return response
   },
   (error) => {
-    const errorResponse = error.response.data as ErrorResponse
+    let errorResponse: ErrorResponse;
+    if (Array.isArray(error.response.data)) {
+      const errorResponseList = error.response.data as ErrorResponseList;
+      errorResponse = errorResponseList[0];
+    } else {
+      // 否则,将其视为单个 ErrorResponse
+      errorResponse = error.response.data as ErrorResponse;
+    }
     handleError(errorResponse)
     return Promise.reject(errorResponse)
   }
@@ -43,8 +47,8 @@ export default {
     const response = await client.post<T>(url, data)
     return response.data as T
   },
-  delete: async (url: string) => {
-    await client.delete(url)
+  delete: async (url: string,params?:any) => {
+    await client.delete(url,{params})
   },
   put: async <T>(url: string, data: Record<string, any> = {}) => {
     const response = await client.put<T>(url, data)

+ 3 - 3
src/composables/useSearchable.ts

@@ -15,9 +15,9 @@ export const useSearchable = <T extends BaseFilterRequest, D extends BaseModel>(
         loading.value = true
         const result = await api({...filter.value, page: pagination.page, size: pagination.size, ...defaultFilter})
         data.value = result.data
-        pagination.page = result.paging.page
-        pagination.size = result.paging.size
-        pagination.total = result.paging.total
+        pagination.page = result.pagination.page
+        pagination.size = result.pagination.size
+        pagination.total = result.pagination.total
         loading.value = false
     }
 

+ 3 - 7
src/layouts/components/AppLogo.vue

@@ -1,14 +1,10 @@
-<script setup lang="ts">
-
-</script>
+<script setup lang="ts"></script>
 
 <template>
   <div class="w-full h-[56px] flex items-center text-2xl cursor-pointer gap-2 font-bold">
     <img src="@/assets/icons/logo.svg" class="w-[32px]" />
-    Yili Admin
+    布兰德后台
   </div>
 </template>
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 4 - 18
src/layouts/components/Header.vue

@@ -1,25 +1,11 @@
 <script setup lang="ts">
-
 import UserArea from '@/layouts/components/UserArea.vue'
-import { useRouter } from 'vue-router'
-
-const router = useRouter()
 </script>
 
 <template>
-    <div class="w-full h-full flex items-center justify-between px-6">
-      <TSpace>
-<!--        <TButton variant="text" shape="square">-->
-<!--          <template #icon> <ViewListIcon /></template>-->
-<!--        </TButton>-->
-        <TButton variant="text" @click="() => router.push({ name: 'platform-manage'
-        })">切换平台</TButton>
-      </TSpace>
-
-      <UserArea></UserArea>
-    </div>
+  <div class="w-full h-full flex items-center justify-end px-6">
+    <UserArea></UserArea>
+  </div>
 </template>
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 4 - 5
src/layouts/components/UserArea.vue

@@ -1,9 +1,10 @@
 <script setup lang="ts">
-
 import { ChevronDownIcon } from 'tdesign-icons-vue-next'
 import type { DropdownOption } from 'tdesign-vue-next'
 import { useAppStore } from '@/stores/app'
+import type { User } from '@/models/user'
 const appStore = useAppStore()
+const currentUser: User = appStore.currentUser
 const options: DropdownOption[] = [
   {
     content: '退出登录',
@@ -20,7 +21,7 @@ const options: DropdownOption[] = [
       <TButton variant="text">
         <div class="flex gap-2 items-center">
           <TAvatar size="24px" image="https://tdesign.gtimg.com/site/avatar.jpg"></TAvatar>
-          依力
+          {{ currentUser.username }}
           <ChevronDownIcon />
         </div>
       </TButton>
@@ -28,6 +29,4 @@ const options: DropdownOption[] = [
   </div>
 </template>
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 14 - 5
src/model/base.ts

@@ -1,11 +1,18 @@
+import type { User } from "./user";
+
 export interface ErrorResponse {
   code: number;
   message: string;
 }
 export interface BaseModel {
-  id: number
-  createdTime: Date
-  updateTime: Date
+  id: string
+  createdTime: string
+  updateTime: string
+}
+
+export interface AuditBaseModel extends BaseModel {
+  createBy: User
+  updateBy: User
 }
 
 export interface Paging {
@@ -18,6 +25,8 @@ export interface BaseFilterRequest extends Partial<Paging>{
 }
 
 export interface PageResult<T> {
-  paging: Paging,
+  pagination: Paging,
   data: T[]
-}
+}
+
+export interface ErrorResponseList extends Array<ErrorResponse> {}

+ 23 - 0
src/model/category.ts

@@ -0,0 +1,23 @@
+import type { AuditBaseModel,Paging } from '@/model/base'
+
+
+export interface Category extends AuditBaseModel{
+  name: string
+  code:string
+  order: number
+}
+
+
+export interface CategorySearchFilter extends Partial<Paging>{
+  order?: string
+}
+
+export interface CategoryDeleteRequest{
+  id:string
+}
+
+export interface UpdateCategoryRequest{
+  name: string
+  code:string
+  order: number
+}

+ 0 - 8
src/model/classify.ts

@@ -1,8 +0,0 @@
-import type { BaseModel } from '@/model/base'
-
-export interface Classify extends BaseModel{
-  numbering:string
-  name: string
-  sort: string
-}
-

+ 1 - 1
src/model/token.ts

@@ -1,4 +1,4 @@
 export interface CreateTokenRequest {
-  email: string
+  username: string
   password: string
 }

+ 6 - 9
src/model/user.ts

@@ -1,15 +1,12 @@
-import type { BaseFilterRequest, BaseModel } from '@/model/base'
-import type { Role } from '@/model/role'
+import type { BaseFilterRequest, AuditBaseModel } from '@/model/base'
 
-export interface User extends BaseModel{
-  name: string
-  email: string
-  avatar: string
-  roles: Role[]
-  permissions: string[]
+export interface User extends AuditBaseModel{
+  username: string
+  locked: boolean
+  enabled: boolean
 }
 
 
 export interface UserSearchFilter extends BaseFilterRequest {
-  name?: string
+  username?: string
 }

+ 36 - 18
src/pages/classify/components/ClassifyDialog.vue → src/pages/category/components/CategoryDialog.vue

@@ -5,36 +5,45 @@
     :confirmBtn="confirmBtn"
     :closeOnOverlayClick="false"
     @close="handleCloseDialog"
-    @confirm="fetchSaveClassifyData"
+    @confirm="SaveCategoryData"
   >
     <t-space direction="vertical" style="width: 100%">
-      <TForm ref="form" :data="classifyData" :rules="rules" resetType="initial">
+      <TForm ref="form" :data="categoryData" :rules="rules" resetType="initial">
         <TFormItem label="分类名称:" name="name" help="名称长度小于5个汉字,大于2个汉字">
-          <t-input v-model.trim="classifyData.name" clearable placeholder="请输入分类名称" />
+          <t-input v-model.trim="categoryData.name" clearable placeholder="请输入分类名称" />
         </TFormItem>
-        <TFormItem label="分类编号:" name="numbering" help="编号由大于4个字符的英文字母组成">
-          <t-input v-model.trim="classifyData.numbering" clearable placeholder="请输入分类编号" />
+        <TFormItem label="分类编号:" name="code" help="编号由大于4个字符的英文字母组成">
+          <t-input v-model.trim="categoryData.code" clearable placeholder="请输入分类编号" />
         </TFormItem>
-        <TFormItem label="排序级别:" name="sort" help="排序级别由正整数组成">
-          <t-input v-model.trim="classifyData.sort" clearable placeholder="请输入排序级别" />
+        <TFormItem label="排序级别:" name="order" help="排序级别由正整数组成">
+          <t-input-number
+            :min="0"
+            name="order"
+            theme="normal"
+            class="!w-full"
+            placeholder="请输入排序级别"
+            v-model.trim="categoryData.order"
+          ></t-input-number>
         </TFormItem>
       </TForm>
     </t-space>
   </t-dialog>
 </template>
 <script lang="ts" setup>
-import type { Classify } from '@/model/classify'
+import type { Category } from '@/model/category'
 import { computed, ref, reactive, watch } from 'vue'
+import { createCategoryRequest, updateCategoryByIdRequest } from '@/api/category'
 import type { FormInstanceFunctions, FormProps } from 'tdesign-vue-next'
+import { MessagePlugin } from 'tdesign-vue-next'
 const props = defineProps<{
   isEdit?: Boolean | null
   headerTitle?: String | null
-  classify: Classify | {}
+  category: Category | {}
 }>()
 const form = ref<FormInstanceFunctions | null>(null)
 const header = computed(() => `${props.isEdit ? '编辑' : '创建'}${props.headerTitle || ''}`)
 const confirmBtn = computed(() => (props.isEdit ? '保存' : '确定'))
-const classifyData: FormProps['data'] = reactive({ name: '', sort: 0 })
+const categoryData: FormProps['data'] = reactive({ name: '', order: 1 })
 const rules: FormProps['rules'] = {
   name: [
     {
@@ -66,7 +75,7 @@ const rules: FormProps['rules'] = {
       trigger: 'blur'
     }
   ],
-  numbering: [
+  code: [
     {
       required: true,
       message: '分类编号不能为空',
@@ -96,7 +105,7 @@ const rules: FormProps['rules'] = {
       trigger: 'blur'
     }
   ],
-  sort: [
+  order: [
     {
       required: true,
       message: '排序级别不能为空',
@@ -122,18 +131,27 @@ const rules: FormProps['rules'] = {
   ]
 }
 watch(
-  () => props.classify,
-  (newClassify) => {
-    Object.assign(classifyData, newClassify)
+  () => props.category,
+  (newCategory) => {
+    Object.assign(categoryData, newCategory)
   },
   { deep: true }
 )
-const fetchSaveClassifyData = async () => {
-  /* TODO: 保存分类数据 */
+const emit = defineEmits<{
+  success: [void]
+}>()
+const SaveCategoryData = async () => {
   if (form.value) {
     const valid = await form.value.validate()
     if (valid && typeof valid === 'boolean') {
-      /* TODO: 校验通过保存分类数据 */
+      const { code, name, order, id } = categoryData
+      if (!props.isEdit) {
+        await createCategoryRequest({ code, name, order })
+      } else {
+        await updateCategoryByIdRequest(id, { code, name, order })
+      }
+      MessagePlugin.success(`${props.isEdit ? '编辑' : '创建'}分类成功`)
+      emit('success')
       return
     }
   }

+ 121 - 0
src/pages/category/index.vue

@@ -0,0 +1,121 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import type { Category, CategorySearchFilter } from '@/model/category'
+import RegularPage from '@/components/RegularPage.vue'
+import { searchCategoryRequest, deleteCategoryRequest } from '@/api/category'
+import type { BaseTableColumns } from 'tdesign-vue-next'
+import { useSearchable } from '@/composables/useSearchable'
+import CategoryDialog from '@/pages/category/components/CategoryDialog.vue'
+import { MessagePlugin } from 'tdesign-vue-next'
+
+const { data, loading, pagination, onPageChange, fetchData } = useSearchable<
+  CategorySearchFilter,
+  Category
+>(searchCategoryRequest)
+const columns: BaseTableColumns = [
+  {
+    title: '分类名称',
+    colKey: 'name',
+    width: 100
+  },
+  {
+    title: '分类编号',
+    colKey: 'code',
+    width: 100
+  },
+  {
+    title: '排序',
+    colKey: 'order',
+    width: 100
+  },
+  {
+    title: '操作',
+    colKey: 'operation',
+    width: 100
+  }
+]
+const categoryDialogVisible = ref(false)
+const isEdit = ref(false)
+const currentTableData = ref<Category | {}>({})
+onMounted(fetchData)
+const editClick = (category: Category) => {
+  isEdit.value = true
+  currentTableData.value = category
+  categoryDialogVisible.value = true
+}
+const handleCategoryDialogSuccess = () => {
+  fetchData()
+  categoryDialogVisible.value = false
+}
+const handleCategoryDialogClosed = () => {
+  isEdit.value = false
+  currentTableData.value = {}
+}
+const deleteCategoryClick = async (category: Category) => {
+  await deleteCategoryRequest({ id: category.id })
+  fetchData()
+  MessagePlugin.success('删除分类成功')
+}
+</script>
+
+<template>
+  <RegularPage title="分类管理">
+    <template #right-area>
+      <TButton @click="categoryDialogVisible = true">创建分类</TButton>
+    </template>
+    <TTable
+      class="w-full h-full"
+      row-key="id"
+      height="92%"
+      table-layout="auto"
+      :data="data"
+      :loading="loading"
+      :columns="columns"
+      cell-empty-content="-"
+      :paginationAffixedBottom="true"
+      :pagination="{
+        total: pagination.total,
+        current: pagination.page,
+        pageSize: pagination.size,
+        onChange: onPageChange
+      }"
+    >
+      <template #roles="{ row }">
+        <TSpace>
+          <TTag v-for="role in row.roles" :key="role.id" theme="success" variant="light">{{
+            role.label
+          }}</TTag>
+        </TSpace>
+      </template>
+      <template #operation="{ row }">
+        <TSpace :size="1">
+          <TButton variant="text" size="small" theme="primary" @click="editClick(row)"
+            >编辑</TButton
+          >
+          <t-popconfirm
+            theme="default"
+            content="确认删除此项分类吗"
+            @confirm="deleteCategoryClick(row)"
+          >
+            <TButton variant="text" size="small" theme="danger">删除</TButton>
+          </t-popconfirm>
+        </TSpace>
+      </template>
+    </TTable>
+    <CategoryDialog
+      headerTitle="分类"
+      :isEdit="isEdit"
+      :category="currentTableData"
+      v-model:visible="categoryDialogVisible"
+      @closed="handleCategoryDialogClosed"
+      @success="handleCategoryDialogSuccess"
+    ></CategoryDialog>
+  </RegularPage>
+</template>
+
+<style scoped>
+:deep(.t-card__body) {
+  flex: 1;
+  min-height: 0;
+}
+</style>

+ 0 - 107
src/pages/classify/index.vue

@@ -1,107 +0,0 @@
-<script setup lang="ts">
-import RegularPage from '@/components/RegularPage.vue'
-import { onMounted, ref, reactive } from 'vue'
-import type { BaseTableColumns } from 'tdesign-vue-next'
-import type { Classify } from '@/model/classify'
-import ClassifyDialog from '@/pages/classify/components/ClassifyDialog.vue'
-
-const columns: BaseTableColumns = [
-  {
-    title: '分类名称',
-    colKey: 'name',
-    width: 100
-  },
-  {
-    title: '分类编号',
-    colKey: 'numbering',
-    width: 100
-  },
-  {
-    title: '排序',
-    colKey: 'sort',
-    width: 100
-  },
-  {
-    title: '操作',
-    colKey: 'operation',
-    width: 100
-  }
-]
-const loading = ref(false)
-const data = ref(
-  Array.from({ length: 1 })
-    .fill({ id: '1', name: '趣味科普', numbering: 'enjoy', sort: '1' })
-    .map((item) => item)
-)
-const pagination = reactive({
-  total: 1,
-  page: 1,
-  size: 10
-})
-const classifyDialogVisible = ref(false)
-const isEdit = ref(false)
-const currentTableData = ref<Classify | {}>({})
-const fetchClassifyTableData = () => {
-  /* TODO: 获取分类表格数据 */
-}
-onMounted(fetchClassifyTableData)
-const editClick = (classify: Classify) => {
-  isEdit.value = true
-  currentTableData.value = classify
-  console.log(classify)
-
-  classifyDialogVisible.value = true
-}
-const onPageChange = () => {
-  /* TODO: 分页切换 */
-}
-</script>
-
-<template>
-  <RegularPage title="分类管理">
-    <template #right-area>
-      <TButton @click="classifyDialogVisible = true">创建分类</TButton>
-    </template>
-    <TTable
-      class="w-full h-full"
-      row-key="id"
-      height="92%"
-      table-layout="auto"
-      :data="data"
-      :loading="loading"
-      :columns="columns"
-      cell-empty-content="-"
-      :paginationAffixedBottom="true"
-      :pagination="{
-        total: pagination.total,
-        current: pagination.page,
-        pageSize: pagination.size,
-        onChange: onPageChange
-      }"
-    >
-      <template #roles="{ row }">
-        <TSpace>
-          <TTag v-for="role in row.roles" :key="role.id" theme="success" variant="light">{{
-            role.label
-          }}</TTag>
-        </TSpace>
-      </template>
-      <template #operation="{ row }">
-        <TButton variant="text" size="small" theme="primary" @click="editClick(row)">编辑</TButton>
-      </template>
-    </TTable>
-    <ClassifyDialog
-      :classify="currentTableData"
-      v-model:visible="classifyDialogVisible"
-      :isEdit="isEdit"
-      headerTitle="分类"
-    ></ClassifyDialog>
-  </RegularPage>
-</template>
-
-<style scoped>
-:deep(.t-card__body) {
-  flex: 1;
-  min-height: 0;
-}
-</style>

+ 5 - 16
src/pages/common/login.vue

@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import Background from '@/pages/common/components/Background.vue'
-import Copyright from '@/components/Copyright.vue'
 import { LockOnIcon, UserIcon } from 'tdesign-icons-vue-next'
 import { reactive, ref } from 'vue'
 import type { CreateTokenRequest } from '@/model/token'
@@ -9,38 +8,33 @@ import { useAppStore } from '@/stores/app'
 
 const loginForm = reactive<CreateTokenRequest>({
   password: '',
-  email: ''
+  username: ''
 })
 
-const canRegister = ref(false)
-
 const loading = ref(false)
 const appStore = useAppStore()
 
 const handleLogin = async () => {
-  if (!loginForm.email || !loginForm.password) {
+  if (!loginForm.username || !loginForm.password) {
     MessagePlugin.error('用户名或密码不能为空')
     return
   }
   try {
+    loading.value = true
     await appStore.login(loginForm)
   } finally {
     loading.value = false
   }
 }
-
 </script>
 
 <template>
   <div class="w-[100vw] h-[100vh] px-20 flex items-center">
     <Background></Background>
     <div class="w-[400px]">
-      <div class="text-4xl font-bold mb-12">欢迎到<br />Yili Admin</div>
-      <div class="mb-12 flex items-center gap-2" v-if="canRegister">
-        买有账号?<TLink>注册新账号</TLink>
-      </div>
+      <div class="text-4xl font-bold mb-12">欢迎到<br />布兰德后台管理系统</div>
       <div class="flex flex-col gap-6 mb-11">
-        <TInput size="large" placeholder="用户名/邮箱" v-model="loginForm.email" clearable >
+        <TInput size="large" placeholder="账户" v-model="loginForm.username" clearable>
           <template #prefix-icon>
             <UserIcon></UserIcon>
           </template>
@@ -57,15 +51,10 @@ const handleLogin = async () => {
             <LockOnIcon></LockOnIcon>
           </template>
         </TInput>
-        <div class="flex justify-between items-center">
-          <TCheckbox>记住账号</TCheckbox>
-          <TLink>忘记密码?</TLink>
-        </div>
       </div>
       <TButton size="large" :block="true" @click="handleLogin" :loading="loading">
         {{ loading ? '登录中' : '登录' }}
       </TButton>
-      <Copyright />
     </div>
   </div>
 </template>

+ 4 - 4
src/router/index.ts

@@ -6,9 +6,9 @@ import Layout from '@/layouts/Layout.vue'
 
 export const asyncRoutes: RouteRecordRaw[] = [
   {
-    name: 'classify',
-    path: 'classify',
-    component: () => import('@/pages/classify/index.vue'),
+    name: 'category',
+    path: 'category',
+    component: () => import('@/pages/category/index.vue'),
     meta: {
       title: '分类管理',
       icon: 'system-3'
@@ -24,7 +24,7 @@ const router = createRouter({
       component: Layout,
       path: '/',
       redirect: {
-        name: 'dashboard'
+        name: 'category'
       },
       children: asyncRoutes
     },