Browse Source

✨ feat: 增加乐跑赠送次数审核功能

Pchen0 2 months ago
parent
commit
acf612ec85

+ 1 - 2
.env

@@ -1,4 +1,3 @@
-NODE_ENV=production
 VITE_APP_API_BASE_URL=https://lepao-api.xxoo365.top
 VITE_APP_API_BASE_URL=https://lepao-api.xxoo365.top
 VITE_RSA_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6Z1lGYzVRMGVqbTh4akZsSjdMSQpBZDJGeC9TalM0OWQ5emwyZHlaNzNDMGZLU3Fuc1pJQUZkREpWZWV6bUp6T1hOWFdoYVZHaHFwM0dCUWVvcDBKClIxekZ3bUs1em9ReElTTDc5WVF3SmxoSjdaellhL0xNcGtGZDRDVFQ4UzUwTGFzN1FpcUtqRE1BQjFLZEpaTnIKNE5HcjNUWVV4MVVpTzlUTW9YV3lBdFZRQVN2a3lFSVFIb3B4T2Vod0ZuNGRhVE8vLzF5TXRyNnZoclE4enJRMwpxUG01YWJmY0lRM3B1WDVJd1MrekRmSkI5Rm9rc0paa3RWNHI2KzM2U1E3WGp2MDFBQjJvK20yejZqNzNuWjQ1Ci95TEx5NmZHTG5lTWpTaUxHMDhNUWFCUjV1dTNITTRnMkpnanA4eU10Rkg1Tkc5Zys1dXRhTDNzd3JGQjhxd1UKdFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
 VITE_RSA_PUBLIC_KEY=LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6Z1lGYzVRMGVqbTh4akZsSjdMSQpBZDJGeC9TalM0OWQ5emwyZHlaNzNDMGZLU3Fuc1pJQUZkREpWZWV6bUp6T1hOWFdoYVZHaHFwM0dCUWVvcDBKClIxekZ3bUs1em9ReElTTDc5WVF3SmxoSjdaellhL0xNcGtGZDRDVFQ4UzUwTGFzN1FpcUtqRE1BQjFLZEpaTnIKNE5HcjNUWVV4MVVpTzlUTW9YV3lBdFZRQVN2a3lFSVFIb3B4T2Vod0ZuNGRhVE8vLzF5TXRyNnZoclE4enJRMwpxUG01YWJmY0lRM3B1WDVJd1MrekRmSkI5Rm9rc0paa3RWNHI2KzM2U1E3WGp2MDFBQjJvK20yejZqNzNuWjQ1Ci95TEx5NmZHTG5lTWpTaUxHMDhNUWFCUjV1dTNITTRnMkpnanA4eU10Rkg1Tkc5Zys1dXRhTDNzd3JGQjhxd1UKdFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
-VITE_APP_VERSION=2.0
+VITE_APP_VERSION=2.1

+ 37 - 1
src/api/goods.js

@@ -5,8 +5,12 @@ const api = {
   GoodsList: '/Goods/List',
   GoodsList: '/Goods/List',
   GetCount: '/Goods/GetCount',
   GetCount: '/Goods/GetCount',
   SendCount: '/Goods/SendCount',
   SendCount: '/Goods/SendCount',
+  MySendCountRequestList: '/Goods/SendCountRequest/MyList',
   AdminGoods: '/Admin/Goods',
   AdminGoods: '/Admin/Goods',
-  AdminGoodsList: '/Admin/Goods/List'
+  AdminGoodsList: '/Admin/Goods/List',
+  AdminSendCountRequestList: '/Admin/Goods/SendCountRequest/List',
+  AdminApproveSendCountRequest: '/Admin/Goods/SendCountRequest/Approve',
+  AdminRejectSendCountRequest: '/Admin/Goods/SendCountRequest/Reject'
 }
 }
 
 
 export function getGoods (parameter) {
 export function getGoods (parameter) {
@@ -33,6 +37,14 @@ export function sendCount (parameter) {
   })
   })
 }
 }
 
 
+export function getMySendCountRequestList (parameter) {
+  return request({
+    url: api.MySendCountRequestList,
+    method: 'get',
+    params: parameter
+  })
+}
+
 export function adminGetGoods (parameter) {
 export function adminGetGoods (parameter) {
   return request({
   return request({
     url: api.AdminGoods,
     url: api.AdminGoods,
@@ -64,3 +76,27 @@ export function getCount (parameter) {
     params: parameter
     params: parameter
   })
   })
 }
 }
+
+export function adminSendCountRequestList (parameter) {
+  return request({
+    url: api.AdminSendCountRequestList,
+    method: 'get',
+    params: parameter
+  })
+}
+
+export function adminApproveSendCountRequest (parameter) {
+  return request({
+    url: api.AdminApproveSendCountRequest,
+    method: 'post',
+    data: parameter
+  })
+}
+
+export function adminRejectSendCountRequest (parameter) {
+  return request({
+    url: api.AdminRejectSendCountRequest,
+    method: 'post',
+    data: parameter
+  })
+}

+ 10 - 1
src/components/userCard/userCard.vue

@@ -13,6 +13,9 @@
       <a-button type="primary" size="large" @click="SendCount()">
       <a-button type="primary" size="large" @click="SendCount()">
         <span><icon-gift /> 赠送次数</span>
         <span><icon-gift /> 赠送次数</span>
       </a-button>
       </a-button>
+      <a-button size="large" @click="goSendCountRecords">
+        <span><icon-list /> 赠送记录</span>
+      </a-button>
     </a-space>
     </a-space>
   </a-card>
   </a-card>
 
 
@@ -31,6 +34,7 @@
 
 
 <script setup>
 <script setup>
 import { ref, reactive, onUnmounted, onMounted } from 'vue'
 import { ref, reactive, onUnmounted, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
 import { getCount, sendCount } from '@/api/goods'
 import { getCount, sendCount } from '@/api/goods'
 import { Notification, Message } from '@arco-design/web-vue'
 import { Notification, Message } from '@arco-design/web-vue'
 
 
@@ -45,6 +49,7 @@ const userCount = ref({
   lepao_count: 0
   lepao_count: 0
 })
 })
 const loading = ref(false)
 const loading = ref(false)
+const router = useRouter()
 
 
 const visible = ref(false)
 const visible = ref(false)
 const ok_loading = ref(false)
 const ok_loading = ref(false)
@@ -78,7 +83,7 @@ const handleBeforeOk = async (done) => {
       return false
       return false
     }
     }
 
 
-    Message.success('赠送成功!')
+    Message.success('已提交审核,审核通过后接收方将到账')
     done()
     done()
     GetCount()
     GetCount()
   } catch (error) {
   } catch (error) {
@@ -102,6 +107,10 @@ const SendCount = async () => {
   visible.value = true
   visible.value = true
 }
 }
 
 
+const goSendCountRecords = () => {
+  router.push('/store/sendCountRecords')
+}
+
 const GetCount = async () => {
 const GetCount = async () => {
   try {
   try {
     const res = await getCount()
     const res = await getCount()

+ 281 - 0
src/pages/admin/goods/sendCountRequestList.vue

@@ -0,0 +1,281 @@
+<template>
+  <div class="container">
+    <Breadcrumb :items="['网站管理', '赠送次数审核']" />
+
+    <a-card title="赠送审核列表">
+      <a-row>
+        <a-col :flex="1">
+          <a-form :model="queryData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }" label-align="left">
+            <a-row :gutter="16">
+              <a-col :span="8">
+                <a-form-item field="status" label="审核状态">
+                  <a-select v-model="queryData.status" :options="statusOptions" placeholder="请选择状态" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </a-form>
+        </a-col>
+        <a-divider style="height: 84px" direction="vertical" />
+        <a-col :flex="'86px'" style="text-align: right">
+          <a-space direction="vertical" :size="18">
+            <a-button type="primary" @click="search">
+              <template #icon>
+                <icon-search />
+              </template>
+              搜索
+            </a-button>
+            <a-button @click="reset">
+              <template #icon>
+                <icon-refresh />
+              </template>
+              重置
+            </a-button>
+          </a-space>
+        </a-col>
+      </a-row>
+
+      <a-table
+        :data="data"
+        stripe
+        hoverable
+        column-resizable
+        class="table"
+        :loading="loading"
+        :columns="columns"
+        :pagination="{
+          showPageSize: true,
+          showJumper: true,
+          showTotal: true,
+          pageSize: pagination.pagesize,
+          current: pagination.current,
+          total: pagination.total
+        }"
+        @page-change="handlePageChange"
+        @page-size-change="handlePageSizeChange"
+      >
+        <template #created_at="{ record }">
+          {{ stramptoTime(record.created_at) }}
+        </template>
+        <template #reviewed_at="{ record }">
+          {{ stramptoTime(record.reviewed_at) }}
+        </template>
+        <template #status="{ record }">
+          <a-tag color="orangered" v-if="record.status === 'pending'">待审核</a-tag>
+          <a-tag color="green" v-else-if="record.status === 'approved'">已通过</a-tag>
+          <a-tag color="red" v-else-if="record.status === 'rejected'">已拒绝</a-tag>
+        </template>
+        <template #reject_reason="{ record }">
+          {{ record.reject_reason || '-' }}
+        </template>
+        <template #optional="{ record }">
+          <a-space v-if="record.status === 'pending'">
+            <a-button type="primary" size="small" @click="approveRequest(record.id)">通过</a-button>
+            <a-button status="danger" size="small" @click="openRejectModal(record.id)">拒绝</a-button>
+          </a-space>
+          <span v-else>-</span>
+        </template>
+      </a-table>
+    </a-card>
+
+    <a-modal
+      v-model:visible="rejectModalVisible"
+      title="拒绝赠送申请"
+      @before-ok="handleRejectBeforeOk"
+      @cancel="handleRejectCancel"
+    >
+      <a-form :model="rejectForm">
+        <a-form-item field="reason" label="拒绝原因">
+          <a-textarea v-model="rejectForm.reason" :max-length="255" placeholder="选填,最多255字" allow-clear />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref, onMounted } from 'vue'
+import { Notification } from '@arco-design/web-vue'
+import {
+  adminApproveSendCountRequest,
+  adminRejectSendCountRequest,
+  adminSendCountRequestList
+} from '@/api/goods'
+
+const loading = ref(false)
+const data = ref([])
+
+const pagination = reactive({
+  total: 0,
+  current: 1,
+  pagesize: 20
+})
+
+const statusOptions = [
+  { label: '全部', value: '' },
+  { label: '待审核', value: 'pending' },
+  { label: '已通过', value: 'approved' },
+  { label: '已拒绝', value: 'rejected' }
+]
+
+const queryData = reactive({
+  status: ''
+})
+
+const columns = [
+  { title: '申请ID', dataIndex: 'id' },
+  { title: '赠送人', dataIndex: 'sender_username' },
+  { title: '接收人', dataIndex: 'receiver_username' },
+  { title: '赠送次数', dataIndex: 'count' },
+  { title: '状态', slotName: 'status' },
+  { title: '申请时间', slotName: 'created_at' },
+  { title: '审核时间', slotName: 'reviewed_at' },
+  { title: '拒绝原因', slotName: 'reject_reason' },
+  { title: '操作', slotName: 'optional' }
+]
+
+const rejectModalVisible = ref(false)
+const rejectForm = reactive({
+  id: 0,
+  reason: ''
+})
+
+const stramptoTime = (time) => {
+  if (!time) return '-'
+  return new Date(time).toLocaleString('zh-CN', {
+    year: 'numeric',
+    month: '2-digit',
+    day: '2-digit',
+    hour: '2-digit',
+    minute: '2-digit',
+    second: '2-digit'
+  })
+}
+
+const getList = async () => {
+  try {
+    loading.value = true
+    const reqData = {
+      pagesize: pagination.pagesize,
+      current: pagination.current
+    }
+    if (queryData.status) reqData.status = queryData.status
+
+    const res = await adminSendCountRequestList(reqData)
+    if (!res || res.code !== 0) {
+      return Notification.error({
+        title: '获取赠送审核列表失败!',
+        content: res?.msg ?? '请稍后再试'
+      })
+    }
+
+    data.value = res.data || []
+    pagination.total = res.pagination?.total || 0
+  } catch (error) {
+    Notification.error({
+      title: '获取赠送审核列表失败!',
+      content: error.message || '请稍后再试'
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+const approveRequest = async (id) => {
+  try {
+    const res = await adminApproveSendCountRequest({ id })
+    if (!res || res.code !== 0) {
+      return Notification.error({
+        title: '审核通过失败!',
+        content: res?.msg ?? '请稍后再试'
+      })
+    }
+
+    Notification.success({
+      title: '操作成功',
+      content: '该赠送申请已审核通过'
+    })
+    getList()
+  } catch (error) {
+    Notification.error({
+      title: '审核通过失败!',
+      content: error.message || '请稍后再试'
+    })
+  }
+}
+
+const openRejectModal = (id) => {
+  rejectForm.id = id
+  rejectForm.reason = ''
+  rejectModalVisible.value = true
+}
+
+const handleRejectBeforeOk = async () => {
+  try {
+    const res = await adminRejectSendCountRequest({
+      id: rejectForm.id,
+      reject_reason: rejectForm.reason
+    })
+
+    if (!res || res.code !== 0) {
+      Notification.error({
+        title: '拒绝申请失败!',
+        content: res?.msg ?? '请稍后再试'
+      })
+      return false
+    }
+
+    Notification.success({
+      title: '操作成功',
+      content: '该赠送申请已拒绝'
+    })
+    getList()
+    return true
+  } catch (error) {
+    Notification.error({
+      title: '拒绝申请失败!',
+      content: error.message || '请稍后再试'
+    })
+    return false
+  }
+}
+
+const handleRejectCancel = () => {
+  rejectModalVisible.value = false
+}
+
+const search = () => {
+  pagination.current = 1
+  getList()
+}
+
+const reset = () => {
+  pagination.current = 1
+  queryData.status = ''
+  getList()
+}
+
+const handlePageChange = (page) => {
+  pagination.current = page
+  getList()
+}
+
+const handlePageSizeChange = (size) => {
+  pagination.pagesize = size
+  pagination.current = 1
+  getList()
+}
+
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped>
+.container {
+  padding: 0 20px 20px 20px;
+}
+
+.table {
+  margin-top: 15px;
+}
+</style>

+ 202 - 0
src/pages/store/sendCountRecords/index.vue

@@ -0,0 +1,202 @@
+<template>
+  <div class="container">
+    <Breadcrumb :items="['云商城', '赠送记录']" />
+
+    <a-card title="赠送记录">
+      <a-row>
+        <a-col :flex="1">
+          <a-form :model="queryData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
+            label-align="left">
+            <a-row :gutter="16">
+              <a-col :span="8">
+                <a-form-item field="direction" label="记录类型">
+                  <a-select v-model="queryData.direction" :options="directionOptions" placeholder="请选择记录类型" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item field="status" label="审核状态">
+                  <a-select v-model="queryData.status" :options="statusOptions" placeholder="请选择审核状态" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </a-form>
+        </a-col>
+        <a-divider style="height: 84px" direction="vertical" />
+        <a-col :flex="'86px'" style="text-align: right">
+          <a-space direction="vertical" :size="18">
+            <a-button type="primary" @click="search">
+              <template #icon>
+                <icon-search />
+              </template>
+              搜索
+            </a-button>
+            <a-button @click="reset">
+              <template #icon>
+                <icon-refresh />
+              </template>
+              重置
+            </a-button>
+          </a-space>
+        </a-col>
+      </a-row>
+
+      <a-table :data="data" stripe hoverable column-resizable class="table" :loading="loading" :columns="columns"
+        :pagination="{
+          showPageSize: true,
+          showJumper: true,
+          showTotal: true,
+          pageSize: pagination.pagesize,
+          current: pagination.current,
+          total: pagination.total
+        }" @page-change="handlePageChange" @page-size-change="handlePageSizeChange">
+        <template #direction="{ record }">
+          <a-tag color="arcoblue" v-if="record.direction === 'sent'">我送出的</a-tag>
+          <a-tag color="purple" v-else>我收到的</a-tag>
+        </template>
+        <template #counterparty_username="{ record }">
+          <a-avatar :size="30">
+            <img :alt="record.counterparty_username ?? ''" :src="record.counterparty_avatar" />
+            <IconUser v-if="!record.counterparty_avatar" />
+          </a-avatar>
+          {{ record.counterparty_username }}
+        </template>
+        <template #status="{ record }">
+          <a-tag color="orangered" v-if="record.status === 'pending'">待审核</a-tag>
+          <a-tag color="green" v-else-if="record.status === 'approved'">已通过</a-tag>
+          <a-tag color="red" v-else-if="record.status === 'rejected'">已拒绝</a-tag>
+        </template>
+        <template #created_at="{ record }">
+          {{ stramptoTime(record.created_at) }}
+        </template>
+        <template #reviewed_at="{ record }">
+          {{ stramptoTime(record.reviewed_at) }}
+        </template>
+        <template #reject_reason="{ record }">
+          {{ record.reject_reason || '-' }}
+        </template>
+      </a-table>
+    </a-card>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, reactive, ref } from 'vue'
+import { Notification } from '@arco-design/web-vue'
+import { getMySendCountRequestList } from '@/api/goods'
+
+const loading = ref(false)
+const data = ref([])
+
+const pagination = reactive({
+  total: 0,
+  current: 1,
+  pagesize: 20
+})
+
+const directionOptions = [
+  { label: '全部', value: '' },
+  { label: '我送出的', value: 'sent' },
+  { label: '我收到的', value: 'received' }
+]
+
+const statusOptions = [
+  { label: '全部', value: '' },
+  { label: '待审核', value: 'pending' },
+  { label: '已通过', value: 'approved' },
+  { label: '已拒绝', value: 'rejected' }
+]
+
+const queryData = reactive({
+  direction: '',
+  status: ''
+})
+
+const columns = [
+  { title: '类型', slotName: 'direction' },
+  { title: '对方用户', slotName: 'counterparty_username' },
+  { title: '赠送次数', dataIndex: 'count' },
+  { title: '审核状态', slotName: 'status' },
+  { title: '申请时间', slotName: 'created_at' },
+  { title: '审核时间', slotName: 'reviewed_at' },
+  { title: '拒绝原因', slotName: 'reject_reason' }
+]
+
+const stramptoTime = (time) => {
+  if (!time) return '-'
+  return new Date(time).toLocaleString('zh-CN', {
+    year: 'numeric',
+    month: '2-digit',
+    day: '2-digit',
+    hour: '2-digit',
+    minute: '2-digit',
+    second: '2-digit'
+  })
+}
+
+const getList = async () => {
+  try {
+    loading.value = true
+    const reqData = {
+      pagesize: pagination.pagesize,
+      current: pagination.current
+    }
+    if (queryData.direction) reqData.direction = queryData.direction
+    if (queryData.status) reqData.status = queryData.status
+
+    const res = await getMySendCountRequestList(reqData)
+    if (!res || res.code !== 0) {
+      return Notification.error({
+        title: '获取赠送记录失败!',
+        content: res?.msg ?? '请稍后再试'
+      })
+    }
+
+    data.value = res.data || []
+    pagination.total = res.pagination?.total || 0
+  } catch (error) {
+    Notification.error({
+      title: '获取赠送记录失败!',
+      content: error.message || '请稍后再试'
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+const search = () => {
+  pagination.current = 1
+  getList()
+}
+
+const reset = () => {
+  pagination.current = 1
+  queryData.direction = ''
+  queryData.status = ''
+  getList()
+}
+
+const handlePageChange = (page) => {
+  pagination.current = page
+  getList()
+}
+
+const handlePageSizeChange = (size) => {
+  pagination.pagesize = size
+  pagination.current = 1
+  getList()
+}
+
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped>
+.container {
+  padding: 0 20px 20px 20px;
+}
+
+.table {
+  margin-top: 15px;
+}
+</style>

+ 18 - 0
src/router/index.js

@@ -117,6 +117,15 @@ const routes = [
                     title: '我的订单'
                     title: '我的订单'
                 }
                 }
             },
             },
+            {
+                path: 'sendCountRecords',
+                name: 'store.sendCountRecords',
+                component: () => import('../pages/store/sendCountRecords/index.vue'),
+                meta: {
+                    title: '赠送记录',
+                    hideInMenu: true
+                },
+            }
         ]
         ]
     },
     },
     {
     {
@@ -335,6 +344,15 @@ const routes = [
                     title: '请求日志',
                     title: '请求日志',
                     permission: ['admin']
                     permission: ['admin']
                 }
                 }
+            },
+            {
+                path: 'goods/sendCountRequestList',
+                name: 'admin.goods.sendCountRequestList',
+                component: () => import('../pages/admin/goods/sendCountRequestList.vue'),
+                meta: {
+                    title: '赠送审核',
+                    permission: ['admin', 'service']
+                }
             }
             }
         ]
         ]
     },
     },

+ 10 - 1
vite.config.js

@@ -11,6 +11,15 @@ export default defineConfig({
       '@': path.resolve(__dirname, 'src')
       '@': path.resolve(__dirname, 'src')
     }
     }
   },
   },
+  // lru-cache@11 的 ESM 入口含顶层 await;默认 es2020 目标会在预构建/最终转译阶段报错
+  build: {
+    target: 'es2022'
+  },
+  optimizeDeps: {
+    esbuildOptions: {
+      target: 'es2022'
+    }
+  },
   server: {
   server: {
     host: true,
     host: true,
     proxy: {
     proxy: {
@@ -21,4 +30,4 @@ export default defineConfig({
       }
       }
     }
     }
   }
   }
-})
+})