xiongzhu 2 лет назад
Родитель
Сommit
eb2a9444bd

BIN
src/assets/1.jpg


BIN
src/assets/2.jpg


BIN
src/assets/3.jpg


+ 16 - 0
src/enums/index.js

@@ -3,3 +3,19 @@ export const WithdrawStatus = {
     SUCCESS: '完成',
     REJECTED: '拒绝'
 }
+
+export const MemberOrderStatus = {
+    NOT_PAID: '未支付',
+    FINISH: '已完成',
+    CANCEL: '已取消'
+}
+
+export const PayMethod = {
+    WECHAT: '微信',
+    ALIPAY: '支付宝'
+}
+
+export const MemberType = {
+    TRIAL: '试用会员',
+    PAID: '付费会员'
+}

+ 20 - 4
src/router/index.js

@@ -14,6 +14,14 @@ const router = createRouter({
             redirect: '/home',
             component: () => import('../views/MainView.vue'),
             children: [
+                {
+                    path: '404',
+                    name: '404',
+                    component: () => import('../views/NotFoundView.vue'),
+                    meta: {
+                        title: '页面不存在'
+                    }
+                },
                 {
                     path: 'home',
                     name: 'home',
@@ -47,11 +55,19 @@ const router = createRouter({
                     }
                 },
                 {
-                    path: '404',
-                    name: '404',
-                    component: () => import('../views/NotFoundView.vue'),
+                    path: 'memberOrder',
+                    name: 'memberOrder',
+                    component: () => import('../views/MemberOrderView.vue'),
                     meta: {
-                        title: '页面不存在'
+                        title: '会员订单'
+                    }
+                },
+                {
+                    path: 'member',
+                    name: 'member',
+                    component: () => import('../views/MemberView.vue'),
+                    meta: {
+                        title: '会员列表'
                     }
                 }
             ]

+ 5 - 3
src/views/LoginView.vue

@@ -1,7 +1,8 @@
 <template>
-    <ElContainer class="h-full" @keyup.enter="login">
-        <ElMain class="!flex items-center justify-center">
-            <ElCard class="w-5/6 max-w-lg">
+    <ElContainer class="h-full bg-cover bg-center" @keyup.enter="login" :style="{ backgroundImage: `url(${bg})` }">
+        <ElMain class="backdrop-blur1 dark:backdrop-brightness-50 !flex flex-col items-center justify-center">
+            <span class="text-3xl font-[sh] text-white [text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">ChatAdmin</span>
+            <ElCard class="w-full max-w-lg !rounded-xl mt-8 mb-16">
                 <ElForm :model="model" :rules="rules" label-position="top" ref="form">
                     <ElFormItem prop="username" label="用户名">
                         <ElInput v-model="model.username"></ElInput>
@@ -26,6 +27,7 @@ import { useUserStore } from '@/stores/user'
 import { storeToRefs } from 'pinia'
 import { useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
+import bg from '@/assets/2.jpg'
 
 const router = useRouter()
 const { user, setUser } = storeToRefs(useUserStore())

+ 10 - 4
src/views/MainView.vue

@@ -49,7 +49,7 @@
     </ElContainer>
 </template>
 <script setup>
-import { ElAside, ElContainer, ElHeader, ElMain } from 'element-plus'
+import { ElAside, ElContainer, ElHeader, ElMain, ElMessageBox } from 'element-plus'
 import DarkSwitch from '@/components/DarkSwitch.vue'
 import SideMenu from '@/components/SideMenu.vue'
 import { useRoute } from 'vue-router'
@@ -87,7 +87,7 @@ const menus = ref([
         icon: shallowRef(MoodSmile),
         children: [
             {
-                name: 'membership',
+                name: 'member',
                 title: '会员列表'
             },
             {
@@ -127,7 +127,13 @@ function onCommand(cmd) {
     }
 }
 function logout() {
-    http.setToken(null)
-    location.reload()
+    ElMessageBox.confirm('确定退出登录吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+    }).then(() => {
+        http.setToken(null)
+        location.reload()
+    })
 }
 </script>

+ 26 - 0
src/views/MemberOrderView.vue

@@ -0,0 +1,26 @@
+<template>
+    <PagingTable url="/admin/membership/orders" :where="where">
+        <template #filter>
+            <EnumSelect :enum="MemberOrderStatus" v-model="where.status" placeholder="筛选状态" />
+        </template>
+        <ElTableColumn prop="id" label="#" width="80" />
+        <ElTableColumn prop="userId" label="用户ID" min-width="120" />
+        <ElTableColumn prop="planId" label="会员类型" min-width="120" />
+        <ElTableColumn prop="name" label="会员名称" min-width="120" />
+        <ElTableColumn prop="price" label="价格" min-width="120" />
+        <ElTableColumn prop="status" label="状态" :formatter="statusFormatter" width="100" />
+        <ElTableColumn prop="payMethod" label="支付方式" :formatter="payMethodFormatter" width="120" />
+        <ElTableColumn prop="createdAt" label="创建时间" :formatter="timeFormatter" width="150" />
+    </PagingTable>
+</template>
+<script setup>
+import PagingTable from '@/components/PagingTable.vue'
+import { useTimeFormatter, useEnumFormatter } from '@/utils/formatter'
+import { MemberOrderStatus, PayMethod } from '@/enums'
+import EnumSelect from '@/components/EnumSelect.vue'
+import { ref } from 'vue'
+const timeFormatter = useTimeFormatter()
+const statusFormatter = useEnumFormatter(MemberOrderStatus)
+const payMethodFormatter = useEnumFormatter(PayMethod)
+const where = ref({})
+</script>

+ 23 - 6
src/views/MemberPlanView.vue

@@ -1,10 +1,19 @@
 <template>
-    <div class="flex items-center justify-center flex-wrap space-x-4">
-        <ElCard v-for="item in plans" :key="item.id" class="mb-4 !rounded-lg" :body-style="{ padding: 0 }">
-            <div class="w-40 h-60 flex flex-col items-center justify-center">
-                <div class="text-xl text-gray-500">{{ item.name }}</div>
-                <div class="text-2xl mt-8">¥{{ item.price }}</div>
+    <div class="flex items-center justify-center flex-wrap">
+        <ElCard
+            v-for="item in plans"
+            :key="item.id"
+            class="w-56 h-72 lg:w-64 lg:h-96 mx-8 mb-8 !rounded-lg"
+            :body-style="{ padding: 0, height: '100%' }"
+        >
+            <div class="h-full flex flex-col items-center justify-center">
+                <div class="text-base lg:text-xl text-gray-500">{{ item.name }}</div>
+                <div class="text-xl lg:text-3xl mt-8 font-bold">¥{{ item.price }}</div>
                 <div class="text-sm mt-8 text-gray-500">有效期:{{ item.duration }}天</div>
+                <div class="mt-8">
+                    <ElTag :type="item.enabled ? 'success' : 'info'">{{ item.enabled ? '启用' : '禁用' }}</ElTag>
+                    <ElTag class="ml-4" v-if="item.commissionable">分佣</ElTag>
+                </div>
                 <div class="mt-8">
                     <ElButton type="primary" link @click="editPlan(item)">编辑</ElButton>
                     <ElButton type="danger" link @click="deletePlan(item)">删除</ElButton>
@@ -29,6 +38,12 @@
             <ElFormItem prop="tokenLimit" label="Token限制">
                 <ElInput v-model="model.tokenLimit" />
             </ElFormItem>
+            <ElFormItem prop="commissionable" label="分佣">
+                <ElSwitch v-model="model.commissionable" />
+            </ElFormItem>
+            <ElFormItem prop="enabled" label="开启">
+                <ElSwitch v-model="model.enabled" />
+            </ElFormItem>
         </ElForm>
         <template #footer>
             <ElButton :disabled="loading" @click="showEditDialog = false">取消</ElButton>
@@ -69,7 +84,9 @@ async function editPlan(plan) {
             name: '',
             price: '',
             duration: '',
-            tokenLimit: ''
+            tokenLimit: '',
+            commissionable: false,
+            enabled: false
         }
     }
     showEditDialog.value = true

+ 21 - 0
src/views/MemberView.vue

@@ -0,0 +1,21 @@
+<template>
+    <PagingTable url="/admin/membership/members" :where="where">
+        <template #filter>
+            <EnumSelect :enum="MemberType" v-model="where.memberType" placeholder="筛选类型" />
+        </template>
+        <ElTableColumn prop="userId" label="用户ID" min-width="80" />
+        <ElTableColumn prop="memberType" label="会员类型" :formatter="typeFormatter" min-width="120" />
+        <ElTableColumn prop="expireAt" label="到期时间" :formatter="timeFormatter" min-width="150" />
+        <ElTableColumn prop="createdAt" label="创建时间" :formatter="timeFormatter" min-width="150" />
+    </PagingTable>
+</template>
+<script setup>
+import PagingTable from '@/components/PagingTable.vue'
+import { useTimeFormatter, useEnumFormatter } from '@/utils/formatter'
+import { MemberType } from '@/enums'
+import EnumSelect from '@/components/EnumSelect.vue'
+import { ref } from 'vue'
+const timeFormatter = useTimeFormatter()
+const typeFormatter = useEnumFormatter(MemberType)
+const where = ref({})
+</script>