|
|
@@ -0,0 +1,321 @@
|
|
|
+<!-- 邀请码管理 -->
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <PageSearch
|
|
|
+ ref="searchRef"
|
|
|
+ :search-config="searchConfig"
|
|
|
+ @query-click="handleQueryClick"
|
|
|
+ @reset-click="handleResetClick"
|
|
|
+ />
|
|
|
+
|
|
|
+ <PageContent ref="contentRef" :content-config="contentConfig">
|
|
|
+ <template #toolbar="{ toolbarRight, onToolbar, removeIds, cols }">
|
|
|
+ <CrudToolbarLeft
|
|
|
+ :remove-ids="removeIds"
|
|
|
+ :perm-create="['module_system:invitation:create']"
|
|
|
+ :perm-delete="['module_system:invitation:delete']"
|
|
|
+ create-label="生成邀请码"
|
|
|
+ @add="handleOpenGenerateDialog"
|
|
|
+ @delete="onToolbar('delete')"
|
|
|
+ />
|
|
|
+ <div class="data-table__toolbar--right">
|
|
|
+ <CrudToolbarRight :buttons="toolbarRight" :cols="cols" :on-toolbar="onToolbar" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table="{ data, loading, tableRef, onSelectionChange, pagination }">
|
|
|
+ <div class="data-table__content">
|
|
|
+ <el-table
|
|
|
+ :ref="tableRef as any"
|
|
|
+ v-loading="loading"
|
|
|
+ row-key="id"
|
|
|
+ :data="data"
|
|
|
+ height="100%"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ @selection-change="onSelectionChange"
|
|
|
+ >
|
|
|
+ <template #empty>
|
|
|
+ <el-empty :image-size="70" description="暂无数据" />
|
|
|
+ </template>
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'selection')?.show"
|
|
|
+ type="selection"
|
|
|
+ min-width="55"
|
|
|
+ align="center"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'index')?.show"
|
|
|
+ fixed
|
|
|
+ label="序号"
|
|
|
+ min-width="60"
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ {{ (pagination.currentPage - 1) * pagination.pageSize + scope.$index + 1 }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'code')?.show"
|
|
|
+ label="邀请码"
|
|
|
+ prop="code"
|
|
|
+ min-width="160"
|
|
|
+ show-overflow-tooltip
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'status')?.show"
|
|
|
+ label="状态"
|
|
|
+ prop="status"
|
|
|
+ min-width="100"
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">
|
|
|
+ {{ scope.row.status === "0" ? "未使用" : "已使用" }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'description')?.show"
|
|
|
+ label="备注"
|
|
|
+ prop="description"
|
|
|
+ min-width="160"
|
|
|
+ show-overflow-tooltip
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'created_by')?.show"
|
|
|
+ label="创建者"
|
|
|
+ prop="created_by"
|
|
|
+ min-width="120"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'created_time')?.show"
|
|
|
+ label="创建时间"
|
|
|
+ prop="created_time"
|
|
|
+ min-width="180"
|
|
|
+ sortable
|
|
|
+ show-overflow-tooltip
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'used_by')?.show"
|
|
|
+ label="使用者"
|
|
|
+ prop="used_by"
|
|
|
+ min-width="120"
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'used_time')?.show"
|
|
|
+ label="使用时间"
|
|
|
+ prop="used_time"
|
|
|
+ min-width="180"
|
|
|
+ sortable
|
|
|
+ show-overflow-tooltip
|
|
|
+ />
|
|
|
+ <el-table-column
|
|
|
+ v-if="contentCols.find((col) => col.prop === 'operation')?.show"
|
|
|
+ fixed="right"
|
|
|
+ label="操作"
|
|
|
+ align="center"
|
|
|
+ min-width="100"
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ v-hasPerm="['module_system:invitation:delete']"
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ link
|
|
|
+ icon="delete"
|
|
|
+ :disabled="scope.row.status === '1'"
|
|
|
+ @click="handleRowDelete(scope.row.id)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </PageContent>
|
|
|
+
|
|
|
+ <EnhancedDialog
|
|
|
+ v-model="generateDialogVisible"
|
|
|
+ title="生成邀请码"
|
|
|
+ @close="handleCloseGenerateDialog"
|
|
|
+ >
|
|
|
+ <el-form
|
|
|
+ ref="generateFormRef"
|
|
|
+ :model="generateForm"
|
|
|
+ :rules="generateRules"
|
|
|
+ label-suffix=":"
|
|
|
+ label-width="auto"
|
|
|
+ label-position="right"
|
|
|
+ >
|
|
|
+ <el-form-item label="数量" prop="count">
|
|
|
+ <el-input-number v-model="generateForm.count" :min="1" :max="100" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注" prop="description">
|
|
|
+ <el-input
|
|
|
+ v-model="generateForm.description"
|
|
|
+ :maxlength="255"
|
|
|
+ show-word-limit
|
|
|
+ type="textarea"
|
|
|
+ placeholder="选填"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="handleCloseGenerateDialog">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="generateLoading" @click="handleGenerate">
|
|
|
+ 确定
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </EnhancedDialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive } from "vue";
|
|
|
+import CrudToolbarLeft from "@/components/CURD/CrudToolbarLeft.vue";
|
|
|
+import CrudToolbarRight from "@/components/CURD/CrudToolbarRight.vue";
|
|
|
+import PageSearch from "@/components/CURD/PageSearch.vue";
|
|
|
+import PageContent from "@/components/CURD/PageContent.vue";
|
|
|
+import EnhancedDialog from "@/components/CURD/EnhancedDialog.vue";
|
|
|
+import type { IContentConfig, ISearchConfig } from "@/components/CURD/types";
|
|
|
+import { useCrudList } from "@/components/CURD/useCrudList";
|
|
|
+import InvitationAPI, { InvitationTable, InvitationPageQuery } from "@/api/module_system/invitation";
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: "Invitation",
|
|
|
+ inheritAttrs: false,
|
|
|
+});
|
|
|
+
|
|
|
+const { searchRef, contentRef, handleQueryClick, handleResetClick, refreshList } = useCrudList();
|
|
|
+
|
|
|
+const contentCols = reactive<
|
|
|
+ Array<{ prop?: string; label?: string; show?: boolean }>
|
|
|
+>([
|
|
|
+ { prop: "selection", label: "选择框", show: true },
|
|
|
+ { prop: "index", label: "序号", show: true },
|
|
|
+ { prop: "code", label: "邀请码", show: true },
|
|
|
+ { prop: "status", label: "状态", show: true },
|
|
|
+ { prop: "description", label: "备注", show: true },
|
|
|
+ { prop: "created_by", label: "创建者", show: true },
|
|
|
+ { prop: "created_time", label: "创建时间", show: true },
|
|
|
+ { prop: "used_by", label: "使用者", show: true },
|
|
|
+ { prop: "used_time", label: "使用时间", show: true },
|
|
|
+ { prop: "operation", label: "操作", show: true },
|
|
|
+]);
|
|
|
+
|
|
|
+const searchConfig = reactive<ISearchConfig>({
|
|
|
+ permPrefix: "module_system:invitation",
|
|
|
+ colon: true,
|
|
|
+ form: { labelWidth: "auto" },
|
|
|
+ formItems: [
|
|
|
+ {
|
|
|
+ prop: "code",
|
|
|
+ label: "邀请码",
|
|
|
+ type: "input",
|
|
|
+ attrs: { placeholder: "请输入邀请码", clearable: true },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: "status",
|
|
|
+ label: "状态",
|
|
|
+ type: "select",
|
|
|
+ options: [
|
|
|
+ { label: "未使用", value: "0" },
|
|
|
+ { label: "已使用", value: "1" },
|
|
|
+ ],
|
|
|
+ attrs: { placeholder: "请选择状态", clearable: true, style: { width: "167.5px" } },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
+
|
|
|
+const contentConfig = reactive<IContentConfig<InvitationPageQuery>>({
|
|
|
+ permPrefix: "module_system:invitation",
|
|
|
+ pk: "id",
|
|
|
+ cols: contentCols as IContentConfig["cols"],
|
|
|
+ hideColumnFilter: false,
|
|
|
+ toolbar: [],
|
|
|
+ defaultToolbar: ["refresh", "filter"],
|
|
|
+ pagination: {
|
|
|
+ pageSize: 10,
|
|
|
+ pageSizes: [10, 20, 30, 50],
|
|
|
+ },
|
|
|
+ request: { page_no: "page_no", page_size: "page_size" },
|
|
|
+ indexAction: async (params) => {
|
|
|
+ const res = await InvitationAPI.list(params as InvitationPageQuery);
|
|
|
+ return {
|
|
|
+ total: res.data.data.total,
|
|
|
+ list: res.data.data.items,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ deleteAction: async (ids) => {
|
|
|
+ await InvitationAPI.delete(
|
|
|
+ ids
|
|
|
+ .split(",")
|
|
|
+ .map((s) => Number(s.trim()))
|
|
|
+ .filter((n) => !Number.isNaN(n))
|
|
|
+ );
|
|
|
+ },
|
|
|
+ deleteConfirm: {
|
|
|
+ title: "警告",
|
|
|
+ message: "确认删除该项数据?",
|
|
|
+ type: "warning",
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+function handleRowDelete(id: number) {
|
|
|
+ contentRef.value?.handleDelete(id);
|
|
|
+}
|
|
|
+
|
|
|
+// ---- 生成弹窗 ----
|
|
|
+
|
|
|
+const generateDialogVisible = ref(false);
|
|
|
+const generateFormRef = ref();
|
|
|
+const generateLoading = ref(false);
|
|
|
+
|
|
|
+const generateForm = reactive({
|
|
|
+ count: 1 as number,
|
|
|
+ description: "" as string,
|
|
|
+});
|
|
|
+
|
|
|
+const generateRules = reactive({
|
|
|
+ count: [
|
|
|
+ { required: true, message: "请输入生成数量", trigger: "blur" },
|
|
|
+ ],
|
|
|
+});
|
|
|
+
|
|
|
+function handleOpenGenerateDialog() {
|
|
|
+ generateDialogVisible.value = true;
|
|
|
+}
|
|
|
+
|
|
|
+function handleCloseGenerateDialog() {
|
|
|
+ generateDialogVisible.value = false;
|
|
|
+ generateForm.count = 1;
|
|
|
+ generateForm.description = "";
|
|
|
+ generateFormRef.value?.resetFields();
|
|
|
+}
|
|
|
+
|
|
|
+async function handleGenerate() {
|
|
|
+ if (!generateFormRef.value) return;
|
|
|
+ generateFormRef.value.validate(async (valid: any) => {
|
|
|
+ if (!valid) return;
|
|
|
+ generateLoading.value = true;
|
|
|
+ try {
|
|
|
+ await InvitationAPI.generate({
|
|
|
+ count: generateForm.count,
|
|
|
+ description: generateForm.description || undefined,
|
|
|
+ });
|
|
|
+ generateDialogVisible.value = false;
|
|
|
+ generateForm.count = 1;
|
|
|
+ generateForm.description = "";
|
|
|
+ refreshList();
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error(error);
|
|
|
+ } finally {
|
|
|
+ generateLoading.value = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped></style>
|