Browse Source

组件列表、代码编辑器

unknown 1 month ago
parent
commit
0482d07939

+ 5 - 0
env.d.ts

@@ -1 +1,6 @@
 /// <reference types="vite/client" />
+declare module '*.vue' {
+    import { ComponentOptions } from 'vue'
+    const componentOptions: ComponentOptions
+    export default componentOptions
+}

+ 4 - 0
package.json

@@ -14,7 +14,10 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.3.1",
+    "@types/codemirror": "^5.60.15",
     "axios": "^1.7.2",
+    "codemirror": "^5.65.12",
+    "codemirror-editor-vue3": "^2.8.0",
     "dayjs": "^1.11.12",
     "element-plus": "^2.7.8",
     "js-cookie": "^3.0.5",
@@ -24,6 +27,7 @@
     "nprogress": "^0.2.0",
     "path-to-regexp": "^7.1.0",
     "pinia": "^2.1.7",
+    "qiniu-js": "3.1.2",
     "sass": "^1.77.8",
     "screenfull": "^6.0.2",
     "vue": "^3.4.29",

File diff suppressed because it is too large
+ 240 - 204
pnpm-lock.yaml


+ 1 - 0
src/App.vue

@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+// 面板组件管理
 import { useTheme } from "@/hooks/useTheme"
 import { ElNotification } from "element-plus"
 // 将 Element Plus 的语言设置为中文

+ 39 - 0
src/api/subassembly/index.ts

@@ -0,0 +1,39 @@
+import request from "@/utils/service"
+import type * as Table from "./types/index"
+// 分类列表
+/** 增 */
+/** 增 */
+export function createComponentListApi(data: Table.CreateOrUpdateTableRequestData) {
+    return request({
+        url: "/componentList",
+        method: "post",
+        data
+    })
+}
+
+/** 删 */
+export function deleteComponentListApi(params: object) {
+    return request({
+        url: '/componentList',
+        method: "delete",
+        params
+    })
+}
+
+/** 改 */
+export function updateComponentListApi(data: Table.CreateOrUpdateTableRequestData) {
+    return request({
+        url: "/componentList",
+        method: "put",
+        data
+    })
+}
+
+/** 查 */
+export function getComponentListApi(params: any) {
+    return request({
+        url: "/componentList?id=" + params,
+        method: "GET",
+        // params
+    })
+}

+ 30 - 0
src/api/subassembly/types/index.ts

@@ -0,0 +1,30 @@
+export interface CreateOrUpdateTableRequestData {
+    ID?: number
+    name: string
+    image?: string
+    data?: string
+    imageName?: string
+}
+
+export interface TableRequestData {
+    /** 当前页码 */
+    currentPage: number
+    /** 查询条数 */
+    size: number
+    // 组件名称
+    name?: StringConstructor
+}
+
+export interface TableData {
+    id: string
+    ID: string
+    name: string
+    phone: string
+    roles: string
+    status: boolean
+    username: string
+}
+export type TableResponseData = {
+    list: TableData[]
+    total: number
+}

+ 9 - 0
src/api/uploading/index.ts

@@ -0,0 +1,9 @@
+import request from "@/utils/service"
+/** 查上传七牛云返token */
+export function getQiniuToken(params: any) {
+    return request({
+        url: "/qiniu/token",
+        method: "GET",
+        params
+    })
+}

+ 4 - 4
src/components/HeadSearch/index.vue

@@ -13,7 +13,7 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, watch } from "vue"
+import { shallowRef, 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)
@@ -32,15 +32,15 @@ const props = defineProps({
         }
     },
 })
-const searchList: any = ref(props.searchTerms)
-const searchForm: any = ref(props.ruleForm)
+const searchList: any = shallowRef(props.searchTerms)
+const searchForm: any = shallowRef(props.ruleForm)
 const emit = defineEmits(['handleSearch']);
 // 搜索
 const handleSearch = () => {
     emit('handleSearch');
 }
 
-const searchFormRef = ref<FormInstance | null>(null)
+const searchFormRef = shallowRef<FormInstance | null>(null)
 // 重置
 const resetSearch = () => {
     searchFormRef.value?.resetFields()

+ 7 - 7
src/components/forms/index.vue

@@ -35,8 +35,6 @@ const validateForm = async () => {
 }
 // 表单清空数据
 const cleardate = async () => {
-    console.log(23154);
-
     formRef.value?.clearValidate()
 }
 // 暴露方法给父组件
@@ -48,11 +46,13 @@ defineExpose({
 
 <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-row :gutter="10">
+            <el-col :span="item.colWidth" v-for="(item, index) in formNewRules" :key="index">
+                <el-form-item :prop="[item.field]" :label="item.label" :rules="item.rules" v-if="item.type == 'input'">
+                    <el-input v-model="modelForm[item.field]" :placeholder="item.placeholder" />
+                </el-form-item>
+            </el-col>
+        </el-row>
     </el-form>
 </template>
 

+ 17 - 9
src/components/tables/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { reactive, ref, watch } from "vue"
+import { shallowRef, 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"
@@ -26,18 +26,24 @@ const props = defineProps({
         }
     },
 })
-const btnList: any = ref(props.operatingList)
-const tableColumnsData: any = reactive(props.tableColumns)
+const btnList: any = shallowRef([])
+const tableColumnsData: any = shallowRef([])
+const tableDataList: any = shallowRef([])
 const emit = defineEmits(['operationBtn', 'handleUpdate']);
 
 // 表格头部操作按钮事件
-const operationBtn = (event) => {
+const operationBtn = (event: any) => {
     emit('operationBtn', event);
 }
 // 操作按钮事件
-const handleUpdate = (event, type) => {
+const handleUpdate = (event: any, type: any) => {
     emit('handleUpdate', event, type);
 }
+watch(() => [props.operatingList, props.tableColumns, props.tableData], (newValue) => {
+    btnList.value = newValue[0]
+    tableColumnsData.value = newValue[1]
+    tableDataList.value = newValue[2]
+}, { immediate: true, deep: true })
 </script>
 
 <template>
@@ -60,10 +66,11 @@ const handleUpdate = (event, type) => {
             </div>
         </div>
         <div class="table-wrapper">
-            <el-table :data="tableData" row-key="ID" :tree-props="{ children: 'child', hasChildren: 'hasChildren' }" border>
+            <el-table :data="tableDataList" 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'" />
+                        v-if="column.prop == 'selection' || column.prop == 'index'" />
                     <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">
@@ -87,8 +94,9 @@ const handleUpdate = (event, type) => {
         </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" />
+                :total="paginationData.total" :page-size="paginationData.pageSize"
+                :currentPage="paginationData.currentPage" @size-change="handleSizeChange"
+                @current-change="handleCurrentChange" />
         </div>
     </el-card>
 </template>

+ 1 - 1
src/main.ts

@@ -17,6 +17,7 @@ import "vxe-table/lib/style.css"
 import "vxe-table-plugin-element/dist/style.css"
 import "@/styles/index.scss"
 
+import Codemirror from "codemirror-editor-vue3";
 const app = createApp(App)
 
 /** 加载插件 */
@@ -25,7 +26,6 @@ loadPlugins(app)
 loadSvg(app)
 /** 加载自定义指令 */
 loadDirectives(app)
-
 app.use(store).use(router)
 router.isReady().then(() => {
   app.mount("#app")

+ 25 - 23
src/views/component/classification.vue

@@ -4,9 +4,9 @@
         <tables :tableData="tableData" :operatingList="operatingList" :tableColumns="tableColumns"
             @operationBtn="operationBtn" @handleUpdate="handleUpdate"></tables>
         <!-- 新增/修改 -->
-        <el-dialog v-model="dialogVisible" :title="formData.id === undefined ? '新增分类' : '修改分类'" @closed="resetForm"
+        <el-dialog v-model="dialogVisible" :title="formData.id === null ? '新增分类' : '修改分类'" @closed="resetForm"
             width="500px">
-            <forms ref="childRules" :formNewList="classifyForm" :ruleForm="formData"></forms>
+            <forms ref="childRules" :formNewList="classifyForm" :ruleForm="formData" :key="Math.random()"></forms>
             <template #footer>
                 <el-button @click="dialogVisible = false">取消</el-button>
                 <el-button type="primary" @click="handleCreateOrUpdate" :loading="loading">确认</el-button>
@@ -16,7 +16,7 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, watch } from "vue"
+import { reactive, ref, watch, nextTick } 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"
@@ -78,37 +78,40 @@ const classifyForm = ref([{
         trigger: 'blur'
     }]
 }])
-const childRules = ref(null)
+const childRules: any = ref(null)
 const classifyList: any = ref({
     name: '',
 })
 //#region 增
 const DEFAULT_FORM_DATA: CreateOrUpdateTableRequestData = {
-    id: undefined,
+    id: null,
     name: "",
     parentId: 0
 }
 const formData = ref<CreateOrUpdateTableRequestData>(cloneDeep(DEFAULT_FORM_DATA))
 const dialogVisible = ref<boolean>(false)
 
-const operationBtn = (event) => {
+const operationBtn = (event: any) => {
     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 handleUpdate = (event: any, type: any) => {
+    nextTick(() => {
+        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)
+        }
+        console.log(formData.value, 26);
+    })
 }
 const loading = ref<boolean>(false)
 const paginationData: any = ref({})
@@ -141,8 +144,7 @@ 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
+        const api = formData.value.id === null ? createComponentclassApi : updateComponentclassApi
         api(formData.value).then((res: any) => {
             if (res.code == 200) {
                 ElMessage.success("操作成功")
@@ -169,9 +171,9 @@ const handleDelete = (row: TableData) => {
         })
     })
 }
-const resetForm = () => {
-    childRules.value?.cleardate()
+const resetForm = (formEl: FormInstance | undefined) => {
     formData.value = cloneDeep(DEFAULT_FORM_DATA)
+    childRules.value?.cleardate()
 }
 watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
 </script>

+ 376 - 3
src/views/component/componentlist.vue

@@ -1,7 +1,380 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { reactive, ref, watch, nextTick } from "vue"
+import { getComponentclassApi } from "@/api/classify/index"
+import { createComponentListApi, deleteComponentListApi, updateComponentListApi, getComponentListApi } from "@/api/subassembly/index"
+import { getQiniuToken } from "@/api/uploading/index"
+import HeadSearch from "@/components/HeadSearch/index.vue"
+import tables from "@/components/tables/index.vue"
+import { type CreateOrUpdateTableRequestData, type TableData } from "@/api/subassembly/types/index"
+import { ElMessage, ElMessageBox, ElNotification, ElLoading } from "element-plus"
+import { CirclePlus } from "@element-plus/icons-vue"
+import { cloneDeep } from "lodash-es"
+import "codemirror/mode/javascript/javascript.js"
+import Codemirror from "codemirror-editor-vue3"
+import type { CmComponentRef } from "codemirror-editor-vue3"
+import SideBarItem from './sidebarItem.vue';
+import 'codemirror/theme/idea.css'
+import * as qiniu from 'qiniu-js';
+const cmOptions = reactive({
+    autorefresh: true,
+    tabSize: 4,
+    mode: "text/javascript",
+    theme: "idea",
+    line: true,
+    viewportMargin: Infinity, //处理高度自适应时搭配使用
+    highlightDifferences: true,
+    autofocus: false,
+    indentUnit: 4,
+    smartIndent: true,
+    // readOnly: true, // 只读
+    showCursorWhenSelecting: true,
+    firstLineNumber: 1,
+});
+const cmRef = ref<CmComponentRef>()
+const searchTerms = [{
+    type: 'input',
+    label: '名称',
+    field: 'name',
+    placeholder: '请输入组件名称',
+}]
+const searchRuleForm = {
+    name: '',
+}
+const operatingList = [{
+    id: 'add',
+    typeStyle: 'primary',
+    title: '新增组件',
+    icon: CirclePlus,
+}]
+const tableColumns = reactive([
+    { prop: "index", width: '50' },
+    { prop: 'name', label: '组件名称' },
+    {
+        prop: 'action',
+        label: '操作',
+        labelButton: [{
+            type: 'details',
+            label: '详情',
+            style: 'success',
+        }, {
+            type: 'edit',
+            label: '编辑',
+            style: 'primary',
+        }, {
+            type: 'del',
+            label: '删除',
+            style: 'danger',
+        }]
+    }
+])
+const operationType: any = ref('')
+const componentData: any = ref([])
+const treeCurrent: any = ref(null)
+//#region 增
+const DEFAULT_FORM_DATA: CreateOrUpdateTableRequestData = {
+    ID: null,
+    name: "",
+    image: "",
+    data: "",
+    imageName: "",
+}
+const classifyId: any = ref(null)
+const formData = ref<CreateOrUpdateTableRequestData>(cloneDeep(DEFAULT_FORM_DATA))
+const dialogVisible: any = ref<boolean>(false)
+const fileList: any = ref([])
+const operationBtn = (event: any) => {
+    if (classifyId.value) {
+        if (event.id == 'add') {
+            dialogVisible.value = true
+        }
+        nextTick(() => {
+            cmRef.value?.refresh()
+            cmRef.value?.resize('100%', '600px')
+        })
+    } else {
+        ElMessage.warning('请先选择分类')
+    }
+}
+const handleUpdate = (event: any, type: any) => {
+    operationType.value = type.type
+    if (type.type == 'edit' || type.type == 'details') {
+        dialogVisible.value = true
+        formData.value.ID = event.ID
+        formData.value.name = event.name
+        formData.value.image = event.image
+        formData.value.data = event.data
+        formData.value.imageName = event.image_name
+        let imgArr = {
+            name: event.image_name,
+            url: event.image,
+        }
+        fileList.value.push(imgArr)
+    } else if (type.type == 'del') {
+        handleDelete(event)
+    }
+    nextTick(() => {
+        cmRef.value?.refresh()
+        cmRef.value?.resize('100%', '600px')
+    })
+}
+const loading = ref<boolean>(false)
+const addEditLoading = 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) => {
+        if (res.code == 200) {
+            tableData.value = res.data
+            treeCurrent.value = String(tableData.value[0].ID)
+            classifyId.value = tableData.value[0].ID
+            getComponentData(tableData.value[0].ID)
+        }
+    }).catch(() => {
+        tableData.value = []
+    }).finally(() => {
+        loading.value = false
+    })
+}
+// 确定添加
+const handleCreateOrUpdate = async () => {
+    if (formData.value.name) {
+        if (fileList.value.length > 0) {
+            addEditLoading.value = true
+            const arrList = {
+                name: formData.value.name,
+                image: formData.value.image,
+                data: formData.value.data,
+                component_id: classifyId.value,
+                image_name: formData.value.imageName,
+                ID: formData.value.ID
+            }
+            let msgTitle = ''
+            if (formData.value.ID) {
+                delete arrList.component_id
+                msgTitle = '修改成功'
+            } else {
+                delete arrList.ID
+                msgTitle = '添加成功'
+            }
+            const api = formData.value.ID === null ? createComponentListApi : updateComponentListApi
+            api(arrList).then((res: any) => {
+                if (res.code == 200) {
+                    ElMessage.success(msgTitle)
+                    dialogVisible.value = false
+                    getComponentData(classifyId.value)
+                } else {
+                    ElMessage.error(res.message)
+                }
+                addEditLoading.value = false
+            })
+        } else {
+            ElMessage.warning('请上传组件图片')
+        }
+    } else {
+        ElMessage.warning('请输入组件名称')
+    }
+}
+
+//#region 删
+const handleDelete = (row: TableData) => {
+    ElMessageBox.confirm(`正在删除组件:${row.name},确认删除?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+    }).then(() => {
+        deleteComponentListApi({ id: row.ID }).then(() => {
+            ElMessage.success("删除成功")
+            getComponentData(classifyId.value)
+        })
+    })
+}
+// 选择组件分类搜索组件
+const handleOpen = (event: any) => {
+    classifyId.value = event
+    getComponentData(event)
+}
+// 组件列表查询
+const getComponentData = (id) => {
+    loading.value = true
+    getComponentListApi(id).then((res: any) => {
+        if (res.code == 200) {
+            componentData.value = res.data
+        }
+    }).catch((err) => {
+        ElMessage.error(err)
+    }).finally(() => {
+        loading.value = false
+    })
+}
+const fileImage = ref(null)
+// 上传组件图片
+const singleUpload = (event) => {
+    fileImage.value = event.file
+    getQiniuToken(event.file).then((res: any) => {
+        if (res.code == 200) {
+            QiniuYun(res.data)
+        }
+    })
+}
+const QiniuYun = (token) => {
+    const loading = ElLoading.service({
+        lock: true,
+        text: '正在上传文件,请稍后...',
+        background: 'rgba(255, 255, 255, 0.5)',
+        spinner: 'el-icon-loading'
+    })
+    let key = fileImage.value.name
+    var config = {
+        useCdnDomain: false, //表示是否使用 cdn 加速域名,为布尔值,true 表示使用,默认为 false。
+        region: qiniu.region.z2,
+        domain: "https://qiniu.region.z2", //配置好的七牛云域名  如   https://cdn.qniyun.com/
+        chunkSize: 1000, //每个分片的大小,单位mb,默认值3
+        forceDirect: false //直传还是断点续传方式,true为直传
+    };
+    var putExtra: any = {
+        fname: key, //文件原始文件名
+        params: {},
+        mimeType: []
+    };
+    var observable = qiniu.upload(fileImage.value, key, token, putExtra, config);
+    var observer = {
+        next(res) {
+            //上传进度
+            let filePercent = parseInt(res.total.percent)
+            if (filePercent === 100) {
+                //console.log('实例监听对象', res)
+            }
+            //
+        },
+        // 接收上传错误信息
+        error(err) {
+            loading.close()
+            switch (err.code) {
+                case 413:
+                    ElMessage.error('错了哦,图片可能太大了哦')
+                    break
+                case 408:
+                    ElMessage.error('错了哦,请求超时')
+                    break
+                default:
+                    ElMessage.error('错了哦,上传错误')
+            }
+        },
+        // 接收上传完成后的信息
+        complete(com) {
+            loading.close()
+            formData.value.image = com.key
+            formData.value.imageName = key
+        }
+    };
+    observable.subscribe(observer);
+}
+// 超出限制
+const onExceed = (event) => {
+    ElNotification({
+        title: '警告',
+        message: '超出最大上传限制',
+        type: 'warning',
+    })
+}
+const resetForm = () => {
+    formData.value = cloneDeep(DEFAULT_FORM_DATA)
+    fileList.value = []
+}
+watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
+</script>
 
 <template>
-    <div>test列表</div>
+    <div class="app-container">
+        <HeadSearch :searchTerms="searchTerms" :ruleForm="searchRuleForm" @handleSearch="handleSearch"></HeadSearch>
+        <div class="card_content_interior">
+            <div class="left_interior">
+                <el-menu :default-active="treeCurrent" @select="handleOpen">
+                    <SideBarItem :routerList="tableData" />
+                </el-menu>
+            </div>
+            <div class="right_interior">
+                <tables v-loading="loading" :tableData="componentData" :operatingList="operatingList"
+                    :tableColumns="tableColumns" @operationBtn="operationBtn" @handleUpdate="handleUpdate"></tables>
+            </div>
+        </div>
+        <!-- 新增/修改 -->
+        <div class="card_module" v-loading="addEditLoading">
+            <el-dialog v-model="dialogVisible" :title="formData.ID === null ? '新增组件' : '修改组件'" @closed="resetForm"
+                width="90%">
+                <div>
+                    <div class="componet_Name">
+                        <span style="flex: none;margin-right: 10px;">组件名称</span>
+                        <el-input v-model="formData.name" placeholder="请输入组件名称" />
+                    </div>
+                    <el-upload action="#" v-model:file-list="fileList" show-file-list list-type="picture" :limit="1"
+                        :http-request="singleUpload" :on-exceed="onExceed">
+                        <el-button type="primary">上传组件图片</el-button>
+                    </el-upload>
+                    <Codemirror ref="cmRef" v-model:value="formData.data" :options="cmOptions" border></Codemirror>
+                </div>
+                <template #footer>
+                    <el-button @click="dialogVisible = false">取消</el-button>
+                    <el-button type="primary" @click="handleCreateOrUpdate" :loading="loading">确认</el-button>
+                </template>
+            </el-dialog>
+        </div>
+    </div>
 </template>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.card_content_interior {
+    display: flex;
+    background-color: #fff;
+    border-radius: 5px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
+    margin-top: 15px;
+}
+
+.left_interior {
+    width: 20%;
+    // padding: 10px 0px;
+    // border-right: 1px solid #e4e7ed;
+}
+
+.right_interior {
+    width: 80%;
+}
+
+:deep(.el-card) {
+    border: none !important;
+}
+
+:deep(.span-ellipsis) {
+    font-size: 16px !important;
+}
+
+.span-ellipsis {
+    width: 100%;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    margin-right: 20px;
+}
+
+.componet_Name {
+    width: 300px;
+    display: flex;
+    align-items: center;
+    margin-bottom: 10px;
+}
+
+.card_module :deep(.el-dialog) {
+    margin-top: 4vh
+}
+</style>

+ 29 - 0
src/views/component/sidebarItem.vue

@@ -0,0 +1,29 @@
+<script setup lang="ts">
+import { shallowRef, ref, watch } from "vue"
+const props = defineProps({
+    routerList: {
+        type: Array,
+        default() {
+            return []
+        }
+    },
+});
+const arrList: any = shallowRef(props.routerList)
+watch(() => props.routerList, (newValue) => {
+    arrList.value = newValue
+}, { immediate: true, deep: true })
+
+</script>
+<template>
+    <template v-for="(item, index) in arrList" :key="String(item.ID)">
+        <el-sub-menu :index="String(item.ID)" v-if="item.child && item.child.length > 0">
+            <template #title>{{ item.name }}</template>
+            <sidebarItem :routerList="item.child" />
+        </el-sub-menu>
+        <el-menu-item :index="String(item.ID)" v-else>
+            {{ item.name }}
+        </el-menu-item>
+    </template>
+</template>
+
+<style scoped lang="scss"></style>

+ 13 - 5
tsconfig.app.json

@@ -1,14 +1,22 @@
 {
   "extends": "@vue/tsconfig/tsconfig.dom.json",
-  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
-  "exclude": ["src/**/__tests__/*"],
+  "include": [
+    "env.d.ts",
+    "src/**/*",
+    "src/**/*.vue"
+  ],
+  "exclude": [
+    "src/**/__tests__/*"
+  ],
   "compilerOptions": {
     "composite": true,
     "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
-
     "baseUrl": ".",
+    "moduleResolution": "node",
     "paths": {
-      "@/*": ["./src/*"]
+      "@/*": [
+        "./src/*"
+      ]
     }
   }
-}
+}

+ 9 - 1
tsconfig.json

@@ -1,11 +1,16 @@
 {
   "compilerOptions": {
+    "module": "esnext",
+    "moduleResolution": "node",
     "baseUrl": "./",
     "paths": {
       "@/*": [
         "src/*"
       ]
-    }
+    },
+    "types": [
+      "lodash-es"
+    ]
   },
   "include": [
     "src/**/*.ts",
@@ -16,6 +21,9 @@
   "references": [
     {
       "path": "./tsconfig.node.json"
+    },
+    {
+      "path": "./tsconfig.app.json"
     }
   ]
 }

+ 4 - 3
tsconfig.node.json

@@ -11,9 +11,10 @@
     "composite": true,
     "noEmit": true,
     "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
-
     "module": "ESNext",
     "moduleResolution": "Bundler",
-    "types": ["node"]
+    "types": [
+      "node"
+    ]
   }
-}
+}

+ 1 - 0
vite.config.ts

@@ -10,6 +10,7 @@ export default defineConfig(({ mode }) => {
       vue(),
     ],
     server: {
+      port: 5000,
       proxy: {
         '/api': {
           target: 'http://192.168.11.77:8888',

File diff suppressed because it is too large
+ 41 - 0
vite.config.ts.timestamp-1728457351127-898e9fbc0b3b8.mjs


Some files were not shown because too many files changed in this diff