unknown преди 1 месец
родител
ревизия
2e8cce7b47

+ 15 - 11
index.html

@@ -1,13 +1,17 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="UTF-8">
-    <link rel="icon" href="/favicon.ico">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Vite App</title>
-  </head>
-  <body>
-    <div id="app"></div>
-    <script type="module" src="/src/main.ts"></script>
-  </body>
-</html>
+
+<head>
+  <meta charset="UTF-8">
+  <link rel="icon" href="/logo.png">
+  <!-- <link rel="icon" href="/favicon.ico"> -->
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>面板组件管理后台</title>
+</head>
+
+<body>
+  <div id="app"></div>
+  <script type="module" src="/src/main.ts"></script>
+</body>
+
+</html>

BIN
public/logo.png


+ 38 - 0
src/api/classify/index.ts

@@ -0,0 +1,38 @@
+import request from "@/utils/service"
+import type * as Table from "./types/index"
+// 分类列表
+/** 增 */
+export function createComponentclassApi(data: Table.CreateOrUpdateTableRequestData) {
+    return request({
+        url: "/addcomponentclass",
+        method: "post",
+        data
+    })
+}
+
+/** 删 */
+export function deleteComponentclassApi(params: object) {
+    return request({
+        url: '/componentclass',
+        method: "delete",
+        params
+    })
+}
+
+/** 改 */
+export function updateComponentclassApi(data: Table.CreateOrUpdateTableRequestData) {
+    return request({
+        url: "/componentclass",
+        method: "put",
+        data
+    })
+}
+
+/** 查 */
+export function getComponentclassApi(params: Table.TableRequestData) {
+    return request<Table.TableResponseData>({
+        url: "/componentclass",
+        method: "GET",
+        params
+    })
+}

+ 34 - 0
src/api/classify/types/index.ts

@@ -0,0 +1,34 @@
+export interface CreateOrUpdateTableRequestData {
+  id?: number
+  name: string
+  parentId?: number
+}
+
+export interface TableRequestData {
+  /** 当前页码 */
+  currentPage: number
+  /** 查询条数 */
+  size: number
+  // 组件名称
+  name?: string
+  /** 查询参数:用户名 */
+  username?: string
+  /** 查询参数:手机号 */
+  phone?: string
+}
+
+export interface TableData {
+  createTime: string
+  email: string
+  id: string
+  ID: string
+  name: string
+  phone: string
+  roles: string
+  status: boolean
+  username: string
+}
+export type TableResponseData = {
+  list: TableData[]
+  total: number
+}

+ 59 - 0
src/components/HeadSearch/index.vue

@@ -0,0 +1,59 @@
+<template>
+    <el-card v-loading="loading" shadow="never" class="search-wrapper">
+        <el-form ref="searchFormRef" :inline="true" :model="searchForm">
+            <el-form-item :prop="item.field" :label="item.label" v-for="(item, index) in searchList" :key="index">
+                <el-input v-model="searchForm[`${item.field}`]" :placeholder="item.placeholder" />
+            </el-form-item>
+            <el-form-item>
+                <el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
+                <el-button :icon="Refresh" @click="resetSearch">重置</el-button>
+            </el-form-item>
+        </el-form>
+    </el-card>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, watch } from "vue"
+import { Search, Refresh, CirclePlus, Delete, Download, RefreshRight } from "@element-plus/icons-vue"
+import { type FormInstance, type FormRules, ElMessage, ElMessageBox } from "element-plus"
+const loading = ref<boolean>(false)
+
+const props = defineProps({
+    searchTerms: {
+        type: Array,
+        default() {
+            return []
+        }
+    },
+    ruleForm: {
+        type: Object,
+        default() {
+            return {}
+        }
+    },
+})
+const searchList: any = ref(props.searchTerms)
+const searchForm: any = ref(props.ruleForm)
+const emit = defineEmits(['handleSearch']);
+// 搜索
+const handleSearch = () => {
+    emit('handleSearch');
+}
+
+const searchFormRef = ref<FormInstance | null>(null)
+// 重置
+const resetSearch = () => {
+    searchFormRef.value?.resetFields()
+    handleSearch()
+}
+</script>
+
+<style lang="scss" scoped>
+.search-wrapper {
+    margin-bottom: 20px;
+
+    :deep(.el-card__body) {
+        padding-bottom: 2px;
+    }
+}
+</style>

+ 59 - 0
src/components/forms/index.vue

@@ -0,0 +1,59 @@
+<script setup lang="ts">
+import { defineExpose, ref, watch } from "vue"
+import { type FormInstance, type FormRules, ElMessage, ElMessageBox } from "element-plus"
+const props = defineProps({
+    // 表单数据
+    formNewList: {
+        type: Array,
+        default() {
+            return []
+        }
+    },
+    // 表单绑定值
+    ruleForm: {
+        type: Object,
+        default() {
+            return {}
+        }
+    },
+})
+const formRef = ref<FormInstance | null>(null)
+const formNewRules: any = ref(props.formNewList)
+const modelForm = ref(props.ruleForm)
+// 表单效验
+const flag = ref(false)
+const validateForm = async () => {
+    let valid = await formRef.value.validate().catch(err => {
+        return err
+    })
+    if (valid === true) {
+        flag.value = true
+    } else {
+        flag.value = false
+    }
+    return flag.value
+}
+// 表单清空数据
+const cleardate = async () => {
+    console.log(23154);
+
+    formRef.value?.clearValidate()
+}
+// 暴露方法给父组件
+defineExpose({
+    validateForm,
+    cleardate
+})
+</script>
+
+<template>
+    <el-form ref="formRef" :model="modelForm" label-width="100px" label-position="left">
+        <template v-for="(item, index) in formNewRules" :key="index">
+            <el-form-item :prop="[item.field]" :label="item.label" :rules="item.rules">
+                <el-input v-model="modelForm[item.field]" :placeholder="item.placeholder" />
+            </el-form-item>
+        </template>
+    </el-form>
+</template>
+
+<style lang="scss" scoped></style>

+ 111 - 0
src/components/tables/index.vue

@@ -0,0 +1,111 @@
+<script setup lang="ts">
+import { reactive, ref, watch } from "vue"
+import { Search, Refresh, CirclePlus, Delete, Download, RefreshRight } from "@element-plus/icons-vue"
+import { type CreateOrUpdateTableRequestData, type TableData } from "@/api/table/types/table"
+import { usePagination } from "@/hooks/usePagination"
+const loading = ref<boolean>(false)
+const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
+
+const props = defineProps({
+    operatingList: {
+        type: Array,
+        default() {
+            return []
+        }
+    },
+    tableColumns: {
+        type: Array,
+        default() {
+            return []
+        }
+    },
+    tableData: {
+        type: Array,
+        default() {
+            return []
+        }
+    },
+})
+const btnList: any = ref(props.operatingList)
+const tableColumnsData: any = reactive(props.tableColumns)
+const emit = defineEmits(['operationBtn', 'handleUpdate']);
+
+// 表格头部操作按钮事件
+const operationBtn = (event) => {
+    emit('operationBtn', event);
+}
+// 操作按钮事件
+const handleUpdate = (event, type) => {
+    emit('handleUpdate', event, type);
+}
+</script>
+
+<template>
+    <el-card v-loading="loading" shadow="never">
+        <div class="toolbar-wrapper">
+            <div style="display: flex;align-items: center;">
+                <div style="margin-right: 10px;" v-for="(item, index) in btnList" :key="index">
+                    <el-button :type="item.typeStyle" :icon="item.icon" @click="operationBtn(item)">
+                        {{ item.title }}
+                    </el-button>
+                </div>
+            </div>
+            <div>
+                <el-tooltip content="下载">
+                    <el-button type="primary" :icon="Download" circle />
+                </el-tooltip>
+                <el-tooltip content="刷新当前页">
+                    <el-button type="primary" :icon="RefreshRight" circle />
+                </el-tooltip>
+            </div>
+        </div>
+        <div class="table-wrapper">
+            <el-table :data="tableData" row-key="ID" :tree-props="{ children: 'child', hasChildren: 'hasChildren' }" border>
+                <template v-for="column in tableColumnsData" :key="column.prop">
+                    <el-table-column :type="column.prop" :width="column.width" align="center"
+                        v-if="column.prop == 'selection'" />
+                    <el-table-column :type="column.type" :prop="column.prop" :label="column.label" :width="column.width"
+                        align="center" v-else-if="column.type == 'tag'">
+                        <template #default="scope">
+                            <el-tag v-if="scope.row.roles === 'admin'" type="primary" effect="plain">admin</el-tag>
+                            <el-tag v-else type="warning" effect="plain">{{ scope.row.roles }}</el-tag>
+                        </template>
+                    </el-table-column>
+                    <el-table-column fixed="right" :label="column.label" :width="column.width" align="center"
+                        v-else-if="column.prop == 'action'">
+                        <template #default="scope">
+                            <el-button v-for="disk in column.labelButton" :type="disk.style" text bg size="small"
+                                @click="handleUpdate(scope.row, disk)">
+                                {{ disk.label }}
+                            </el-button>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :type="column.type" :prop="column.prop" :label="column.label" :width="column.width"
+                        align="center" v-else />
+                </template>
+            </el-table>
+        </div>
+        <div class="pager-wrapper" v-if="paginationData.total">
+            <el-pagination background :layout="paginationData.layout" :page-sizes="paginationData.pageSizes"
+                :total="paginationData.total" :page-size="paginationData.pageSize" :currentPage="paginationData.currentPage"
+                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
+        </div>
+    </el-card>
+</template>
+
+<style lang="scss" scoped>
+.toolbar-wrapper {
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 20px;
+}
+
+.table-wrapper {
+    margin-bottom: 20px;
+}
+
+.pager-wrapper {
+    display: flex;
+    justify-content: flex-end;
+}
+</style>

+ 31 - 0
src/router/index.ts

@@ -62,6 +62,37 @@ export const constantRoutes: RouteRecordRaw[] = [
     ]
   },
   {
+
+    path: "/component",
+    component: Layouts,
+    redirect: "/component/classification",
+    name: "component",
+    meta: {
+      title: "组件管理",
+      elIcon: "Grid"
+    },
+    children: [
+      {
+        path: "classification",
+        component: () => import("@/views/component/classification.vue"),
+        name: "classification",
+        meta: {
+          title: "组件分类",
+          keepAlive: true
+        }
+      },
+      {
+        path: "componentlist",
+        component: () => import("@/views/component/componentlist.vue"),
+        name: "componentlist",
+        meta: {
+          title: "组件列表",
+          keepAlive: true
+        }
+      }
+    ]
+  },
+  {
     path: "/table",
     component: Layouts,
     redirect: "/table/element-plus",

+ 4 - 0
src/utils/service.ts

@@ -12,6 +12,10 @@ const request = axios.create({
 })
 // request拦截器
 request.interceptors.request.use(config => {
+  const token = getToken()
+  if (token) {
+    config.headers.Authorization = token
+  }
   return config;
 }, error => {
   Promise.reject(error);

+ 178 - 0
src/views/component/classification.vue

@@ -0,0 +1,178 @@
+<template>
+    <div class="app-container">
+        <HeadSearch :searchTerms="searchTerms" :ruleForm="searchRuleForm" @handleSearch="handleSearch"></HeadSearch>
+        <tables :tableData="tableData" :operatingList="operatingList" :tableColumns="tableColumns"
+            @operationBtn="operationBtn" @handleUpdate="handleUpdate"></tables>
+        <!-- 新增/修改 -->
+        <el-dialog v-model="dialogVisible" :title="formData.id === undefined ? '新增分类' : '修改分类'" @closed="resetForm"
+            width="500px">
+            <forms ref="childRules" :formNewList="classifyForm" :ruleForm="formData"></forms>
+            <template #footer>
+                <el-button @click="dialogVisible = false">取消</el-button>
+                <el-button type="primary" @click="handleCreateOrUpdate" :loading="loading">确认</el-button>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, watch } from "vue"
+import { createComponentclassApi, deleteComponentclassApi, updateComponentclassApi, getComponentclassApi } from "@/api/classify/index"
+import HeadSearch from "@/components/HeadSearch/index.vue"
+import tables from "@/components/tables/index.vue"
+import forms from "@/components/forms/index.vue"
+import { type CreateOrUpdateTableRequestData, type TableData } from "@/api/classify/types/index"
+import { type FormInstance, type FormRules, ElMessage, ElMessageBox } from "element-plus"
+import { Search, Refresh, CirclePlus, Delete, Download, RefreshRight } from "@element-plus/icons-vue"
+import { cloneDeep } from "lodash-es"
+
+const searchTerms = [{
+    type: 'input',
+    label: '名称',
+    field: 'name',
+    placeholder: '请输入组件名称',
+}]
+const searchRuleForm = {
+    name: '',
+}
+const operatingList = [{
+    id: 'add',
+    typeStyle: 'primary',
+    title: '新增分类',
+    icon: CirclePlus,
+}, {
+    id: 'del',
+    typeStyle: 'danger',
+    title: '删除分类',
+    icon: Delete,
+}]
+const tableColumns = reactive([
+    { prop: "selection", width: '50' },
+    { prop: 'name', label: '分类名称' },
+    {
+        prop: 'action',
+        label: '操作',
+        labelButton: [{
+            type: 'add',
+            label: '新增分类',
+            style: 'success',
+        }, {
+            type: 'edit',
+            label: '编辑',
+            style: 'primary',
+        }, {
+            type: 'del',
+            label: '删除',
+            style: 'danger',
+        }]
+    }
+])
+const classifyForm = ref([{
+    field: 'name',
+    label: '分类名称',
+    placeholder: '请输入分类名称',
+    type: 'input',
+    rules: [{
+        required: true,
+        message: '请输入分类名称',
+        trigger: 'blur'
+    }]
+}])
+const childRules = ref(null)
+const classifyList: any = ref({
+    name: '',
+})
+//#region 增
+const DEFAULT_FORM_DATA: CreateOrUpdateTableRequestData = {
+    id: undefined,
+    name: "",
+    parentId: 0
+}
+const formData = ref<CreateOrUpdateTableRequestData>(cloneDeep(DEFAULT_FORM_DATA))
+const dialogVisible = ref<boolean>(false)
+
+const operationBtn = (event) => {
+    if (event.id == 'add') {
+        dialogVisible.value = true
+    }
+}
+const handleUpdate = (event, type) => {
+    console.log(event, type, 77);
+    if (type.type == 'add') {
+        dialogVisible.value = true
+        formData.value.parentId = event.ID
+    } else if (type.type == 'edit') {
+        dialogVisible.value = true
+        formData.value.id = event.ID
+        formData.value.name = event.name
+        // formData.value.parentId = event.ID
+    } else if (type.type == 'del') {
+        handleDelete(event)
+    }
+}
+const loading = ref<boolean>(false)
+const paginationData: any = ref({})
+const tableData = ref([])
+// 搜索
+const handleSearch = () => {
+    getTableData()
+}
+// 列表查询
+const getTableData = () => {
+    loading.value = true
+    getComponentclassApi({
+        currentPage: paginationData.currentPage,
+        size: paginationData.pageSize,
+        name: searchRuleForm.name,
+    }).then((res: any) => {
+        // console.log(res, 25);
+        if (res.code == 200) {
+            // paginationData.total = data.total
+            tableData.value = res.data
+        }
+    }).catch(() => {
+        tableData.value = []
+    }).finally(() => {
+        loading.value = false
+    })
+}
+// 确定添加
+const handleCreateOrUpdate = async () => {
+    let params = await childRules.value.validateForm()
+    if (params) {
+        loading.value = true
+        console.log(formData.value, 24);
+        const api = formData.value.id === undefined ? createComponentclassApi : updateComponentclassApi
+        api(formData.value).then((res: any) => {
+            if (res.code == 200) {
+                ElMessage.success("操作成功")
+                dialogVisible.value = false
+                getTableData()
+            }
+        }).finally(() => {
+            loading.value = false
+        })
+    }
+}
+
+//#region 删
+const handleDelete = (row: TableData) => {
+    ElMessageBox.confirm(`正在删除组件分类:${row.name},确认删除?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+    }).then(() => {
+        console.log(row, 776);
+        deleteComponentclassApi({ id: row.ID }).then(() => {
+            ElMessage.success("删除成功")
+            getTableData()
+        })
+    })
+}
+const resetForm = () => {
+    childRules.value?.cleardate()
+    formData.value = cloneDeep(DEFAULT_FORM_DATA)
+}
+watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
+</script>
+<style lang="scss" scoped></style>

+ 7 - 0
src/views/component/componentlist.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+    <div>test列表</div>
+</template>
+
+<style lang="scss" scoped></style>